Load frequently used packages

library(openxlsx)
library(ggrepel)
library(dplyr)
library(tidyr)
library(data.table)
library(broom)
library(broomExtra)
library(tibble)
library(sjstats)
library(car)
library(lme4)
library(lmerTest)
library(ggplot2)
library(tibble)
library(modelr)
library(tidyverse)
#library(miceadds)
library(ggforce)
require(openxlsx)
library(tidyverse)
library(caret)
library(glmnet)
library(ggplot2)
select <- dplyr::select
filter <- dplyr::filter

Define in/out directory used in this script

dir <- "/Users/shawjes/Dropbox/EspinosaGroup/ANALYSIS/Celiac_MultiOmics/GRS/DSMIG_Shared/Manuscript_Figure1/Data"
dir.Anno.GRSoriginal <- "/Users/shawjes/Dropbox/EspinosaGroup/ANALYSIS/Celiac_MultiOmics/GRS/DSMIG_Shared/Manuscript_Figure1/Annotation/GRSoriginal"
dir.Anno.GRSrevised <- "/Users/shawjes/Dropbox/EspinosaGroup/ANALYSIS/Celiac_MultiOmics/GRS/DSMIG_Shared/Manuscript_Figure1/Annotation/GRSoriginal"

Load MEGA to HTP ID key

setwd(dir)
Warning: The working directory was changed to /Users/shawjes/Dropbox/EspinosaGroup/ANALYSIS/Celiac_MultiOmics/GRS/DSMIG_Shared/Manuscript_Figure1/Data inside a notebook chunk. The working directory will be reset when the chunk is finished running. Use the knitr root.dir option in the setup chunk to change the working directory for notebook chunks.
MEGA.IDkey <- fread("MEGA_041822_MEGA2_to_HTP_ID_key_v0.1_JRS.csv")

MEGA.IDkey

Put the column names representing identifiers into a vector for reference below

ID_colnames <- c("FamilyID", "RecordID", "MEGA.FID", "MEGA.IID", "MEGA.LabID")

Load the analysis metadata

setwd(dir)
Warning: The working directory was changed to /Users/shawjes/Dropbox/EspinosaGroup/ANALYSIS/Celiac_MultiOmics/GRS/DSMIG_Shared/Manuscript_Figure1/Data inside a notebook chunk. The working directory will be reset when the chunk is finished running. Use the knitr root.dir option in the setup chunk to change the working directory for notebook chunks.
meta.MEGA.T21_visit1_Celiac <- fread("MEGA_041822_META_CeliacGRS_v0.1_JRS.csv")

meta.MEGA.T21_visit1_Celiac
For reference later, read in supplemental tables from Sharp et al., 2019
setwd(dir.Anno.GRSoriginal)
Warning: The working directory was changed to /Users/shawjes/Dropbox/EspinosaGroup/ANALYSIS/Celiac_MultiOmics/GRS/DSMIG_Shared/Manuscript_Figure1/Annotation/GRSoriginal inside a notebook chunk. The working directory will be reset when the chunk is finished running. Use the knitr root.dir option in the setup chunk to change the working directory for notebook chunks.
Table_S2 <- read.xlsx("apt15826-sup-0001-supinfo.xlsx", sheet = "Table S2", startRow = 3) %>%
  separate(`Odds.Ratio.[95%.CI]`, into = c("OR", "CI"), sep = " ", extra = "merge", remove = TRUE) %>%
  rename(OR.GRS = OR,
         CI.GRS = CI,
         Weight.GRS = `Weight.(β)`)
Warning: Expected 2 pieces. Missing pieces filled with `NA` in 1 rows [15].
Table_S2

setwd(dir.Anno.GRSoriginal)
Table_S3 <- read.xlsx("apt15826-sup-0001-supinfo.xlsx", sheet = "Table S3", startRow = 3) %>%
  rename(OR.GRS = OR,
         Weight.GRS = `Weight.(β)`)
Table_S3

Read in imputed HLA genotype data provided by Paul Norman

setwd(dir.GRSdata)
Warning: The working directory was changed to /Users/shawjes/Dropbox/EspinosaGroup/ANALYSIS/Celiac_MultiOmics/GRS/DSMIG_Shared/Manuscript_Figure1/Data inside a notebook chunk. The working directory will be reset when the chunk is finished running. Use the knitr root.dir option in the setup chunk to change the working directory for notebook chunks.
hla.imputed <- openxlsx::read.xlsx("EspinosaHLAstudy_HLAcalls_PJN.xlsx")

#hla.imputed %>% colnames()
colnames(hla.imputed)<-c("MEGA.LabID",
                         "Race",
                         "Ethnicity",
                         "sample.id",
                         "HLA-A.1",
                         "HLA-A.2",
                         "HLA-B.1",
                         "HLA-B.2",
                         "HLA-C.1",
                         "HLA-C.2",
                         "HLA-DRB1.1",
                         "HLA-DRB1.2",
                         "HLA-DQA1.1",
                         "HLA-DQA1.2",
                         "HLA-DQB1.1",
                         "HLA-DQB1.2",
                         "HLA-DPB1.1",
                         "HLA-DPB1.2",
                         "Comments")

hla.imputed

# Check that MEGA.LabID == sample.id
hla.imputed %>% filter(MEGA.LabID!=sample.id | is.na(MEGA.LabID) | is.na(sample.id))
# Good

Join with the MEGA to HTP ID key and prepared analysis metadata

hla.imputed01 <- hla.imputed %>%
  # Remove sample.id since it is redundant, as shown above.
  select(-c(sample.id)) %>%
  # Remove metadata columns in the data we got back from Norman's group; to be replaced at time of analysis with latest version of metadata.
  select(-c(Race, Ethnicity)) %>%
  left_join(MEGA.IDkey, by = "MEGA.LabID") %>%
  select(colnames(MEGA.IDkey), everything()) %>%
  select(-c(HTP_participant, In_plink_fam)) %>%
  full_join(meta.MEGA.T21_visit1_Celiac, by = ID_colnames)

# View the data before further manipulation:
hla.imputed01

rm(hla.imputed); gc()
            used   (Mb) gc trigger   (Mb) limit (Mb)  max used   (Mb)
Ncells   5689714  303.9    8726178  466.1         NA   8726178  466.1
Vcells 162921203 1243.0  308334637 2352.5     204800 308334637 2352.5
print("Note: I am purposely calling the LabID column MEGA.LabID because it is an identifier linked to static data. The genetic data shouldn't vary by which sample was used for genotyping (if it does, that's most likely a quality control problem). In analyses using the MEGA genetic data, we will likely want to join the genetic datasets with metadata by the LabID relevant to the other key variables in the analysis. For example, in this project we are looking at Celiac status at visit 1, so when we add in our metadata we will probably want to use age at visit 1 instead of age at time of the sample used for genotyping.")
[1] "Note: I am purposely calling the LabID column MEGA.LabID because it is an identifier linked to static data. The genetic data shouldn't vary by which sample was used for genotyping (if it does, that's most likely a quality control problem). In analyses using the MEGA genetic data, we will likely want to join the genetic datasets with metadata by the LabID relevant to the other key variables in the analysis. For example, in this project we are looking at Celiac status at visit 1, so when we add in our metadata we will probably want to use age at visit 1 instead of age at time of the sample used for genotyping."

Convert HLA genotype dataset into long format

hla.imputed_long <- hla.imputed01 %>%
  gather(key="Locus", value="Allele", `HLA-A.1`:`HLA-DPB1.2`) %>%
  mutate(Locus = gsub("[.]1", "", Locus),
         Locus = gsub("[.]2", "", Locus)) %>%
  select(FamilyID, RecordID, MEGA.FID, MEGA.IID, MEGA.LabID,
         Locus, Allele, Comments, everything())

hla.imputed_long

Verify that each participant has exactly two HLA alleles for each locus

if (hla.imputed_long %>%
  filter(EXCLUDE_from_analysis == 0) %>%
    select(MEGA.IID, Locus, Allele) %>%
    group_by(MEGA.IID, Locus) %>%
    summarise(N=n()) %>%
    arrange(desc(N)) %>%
    filter(N!=2) %>%
    nrow() == 0) {
  print("All IIDs have exactly 2 alleles for each locus. Good!")
  } else {
    print("Oops, not all IIDs have exactly 2 alleles for each locus (and they should).")
  }
`summarise()` has grouped output by 'MEGA.IID'. You can override using the `.groups` argument.
[1] "All IIDs have exactly 2 alleles for each locus. Good!"
if (hla.imputed_long %>%
  filter(EXCLUDE_from_analysis == 0) %>%
    select(RecordID, Locus, Allele) %>%
    group_by(RecordID, Locus) %>%
    summarise(N=n()) %>%
    arrange(desc(N)) %>%
    filter(N!=2) %>%
    nrow() == 0) {
  print("All RecordIDs have exactly 2 alleles for each locus. Good!")
  } else {
    print("Oops, not all RecordIDs have exactly 2 alleles for each locus (and they should).")
  }
`summarise()` has grouped output by 'RecordID'. You can override using the `.groups` argument.
[1] "All RecordIDs have exactly 2 alleles for each locus. Good!"

Double check that the EXCLUDE filter produces a dataset that includes only one set of HLA typing per RecordID

if ( hla.imputed_long %>%
       filter(EXCLUDE_from_analysis == 0) %>%
     select(RecordID, MEGA.LabID) %>%
     unique() %>%
     group_by(RecordID) %>%
     summarise(N_LabIDs = n()) %>%
     filter(N_LabIDs > 1) %>%
     nrow() == 0 ) {
  print("All RecordIDs have only one LabID. Good!")
  } else {
    print("Oops, not all RecordIDs have only one LabID.")
    }
[1] "All RecordIDs have only one LabID. Good!"

View the long format dataset so far

hla.imputed_long

Make a dataset of the HLA-DQ alleles and output to CSV and TSV

analysisData.alleles <- hla.imputed_long %>%
  select(FamilyID, RecordID, MEGA.FID, MEGA.IID, MEGA.LabID, Locus, Allele, EXCLUDE_from_analysis) %>%
  mutate(Locus_Allele = paste(Locus, Allele, sep = "")) %>%
  select(-c(Allele)) %>%
  mutate(Has.Locus_Allele = 1) %>%
  group_by(FamilyID, RecordID, MEGA.FID, MEGA.IID, MEGA.LabID, EXCLUDE_from_analysis, Locus_Allele) %>%
  summarise(Dosage.Locus_Allele = sum(Has.Locus_Allele)) %>%
  ungroup() %>%
  arrange(desc(Dosage.Locus_Allele)) %>%
  spread(key = Locus_Allele, value = Dosage.Locus_Allele) %>%
  gather(key = "Locus_Allele", value = "Dosage.Locus_Allele", `HLA-A*01:01`:`HLA-DRB1*16:02`) %>%
  mutate(Dosage.Locus_Allele = ifelse(is.na(Dosage.Locus_Allele), 0, Dosage.Locus_Allele)) %>%
  separate(Locus_Allele, into = c("Locus", "Allele"), sep = "[*]", extra = "merge", remove = FALSE) %>%
  select(FamilyID, RecordID, MEGA.FID, MEGA.IID, MEGA.LabID, Locus, Locus_Allele, Dosage.Locus_Allele, EXCLUDE_from_analysis, everything())
`summarise()` has grouped output by 'FamilyID', 'RecordID', 'MEGA.FID', 'MEGA.IID', 'MEGA.LabID', 'EXCLUDE_from_analysis'. You can override using
the `.groups` argument.
analysisData.alleles

if (analysisData.alleles %>%
  filter(EXCLUDE_from_analysis == 0) %>%
    select(MEGA.IID, Locus, Locus_Allele, Dosage.Locus_Allele) %>%
    group_by(MEGA.IID, Locus) %>%
    summarise(Total_Dosage.Locus = sum(Dosage.Locus_Allele)) %>%
    arrange(desc(Total_Dosage.Locus)) %>%
    filter(Total_Dosage.Locus != 2) %>%
    nrow() == 0) {
  print("All IIDs have exactly 2 alleles for each locus. Good!")
  } else {
    print("Oops, not all IIDs have exactly 2 alleles for each locus (and they should).")
  }
`summarise()` has grouped output by 'MEGA.IID'. You can override using the `.groups` argument.
[1] "All IIDs have exactly 2 alleles for each locus. Good!"
if (analysisData.alleles %>%
  filter(EXCLUDE_from_analysis == 0) %>%
    select(RecordID, Locus, Locus_Allele, Dosage.Locus_Allele) %>%
    group_by(RecordID, Locus) %>%
    summarise(Total_Dosage.Locus = sum(Dosage.Locus_Allele)) %>%
    arrange(desc(Total_Dosage.Locus)) %>%
    filter(Total_Dosage.Locus != 2) %>%
    nrow() == 0) {
  print("All RecordIDs have exactly 2 alleles for each locus. Good!")
  } else {
    print("Oops, not all RecordIDs have exactly 2 alleles for each locus (and they should).")
  }
`summarise()` has grouped output by 'RecordID'. You can override using the `.groups` argument.
[1] "All RecordIDs have exactly 2 alleles for each locus. Good!"
analysisData.alleles %>%
  split(., .$Locus_Allele) %>%
  lapply(dim) %>%
  unique()
[[1]]
[1] 221  10
  
analysisData.alleles

setwd(dir)
Warning: The working directory was changed to /Users/shawjes/Dropbox/EspinosaGroup/ANALYSIS/Celiac_MultiOmics/GRS/DSMIG_Shared/Manuscript_Figure1/Data inside a notebook chunk. The working directory will be reset when the chunk is finished running. Use the knitr root.dir option in the setup chunk to change the working directory for notebook chunks.
fwrite(analysisData.alleles, "MEGA_041822_Imputed_HLA_Alleles_v0.1_JRS.csv")
fwrite(analysisData.alleles, "MEGA_041822_Imputed_HLA_Alleles_v0.1_JRS.tsv", sep = "\t")

Make a dataset of the HLA-DQ allele groups and output to CSV and TSV

analysisData.alleleGroups <- hla.imputed_long %>%
  select(FamilyID, RecordID, MEGA.FID, MEGA.IID, MEGA.LabID, Locus, Allele, EXCLUDE_from_analysis) %>%
  separate(Allele, into = c("AlleleGroup", "Protein"), sep = "[:]", extra = "merge", remove = FALSE) %>%
  mutate(Locus_AlleleGroup = paste(Locus, AlleleGroup, sep = "")) %>%
  mutate(Has.Locus_AlleleGroup = 1) %>%
  select(FamilyID, RecordID, MEGA.FID, MEGA.IID, MEGA.LabID, Locus_AlleleGroup, Has.Locus_AlleleGroup, EXCLUDE_from_analysis) %>%
  group_by(FamilyID, RecordID, MEGA.FID, MEGA.IID, MEGA.LabID, EXCLUDE_from_analysis, Locus_AlleleGroup ) %>%
  summarise(Dosage.Locus_AlleleGroup = sum(Has.Locus_AlleleGroup)) %>%
  ungroup() %>%
  arrange(desc(Dosage.Locus_AlleleGroup)) %>%
  arrange(Dosage.Locus_AlleleGroup) %>%
  spread(key = Locus_AlleleGroup, value = Dosage.Locus_AlleleGroup) %>%
  gather(key = "Locus_AlleleGroup", value = "Dosage.Locus_AlleleGroup", `HLA-A*01`:`HLA-DRB1*16`) %>%
  mutate(Dosage.Locus_AlleleGroup = ifelse(is.na(Dosage.Locus_AlleleGroup), 0, Dosage.Locus_AlleleGroup)) %>%
  separate(Locus_AlleleGroup, into = c("Locus", "AlleleGroup"), sep = "[*]", extra = "merge", remove = FALSE) %>%
  select(FamilyID, RecordID, MEGA.FID, MEGA.IID, MEGA.LabID, Locus, Locus_AlleleGroup, Dosage.Locus_AlleleGroup, EXCLUDE_from_analysis, everything())
`summarise()` has grouped output by 'FamilyID', 'RecordID', 'MEGA.FID', 'MEGA.IID', 'MEGA.LabID', 'EXCLUDE_from_analysis'. You can override using
the `.groups` argument.
# analysisData.alleleGroups <- hla.imputed_long %>%
#   select(FamilyID, RecordID, MEGA.FID, MEGA.IID, MEGA.LabID, Locus, Allele, EXCLUDE_from_analysis) %>%
#   separate(Allele, into = c("AlleleGroup", "Protein"), sep = "[:]", extra = "merge", remove = FALSE) %>%
#   mutate(Locus_AlleleGroup = paste(Locus, AlleleGroup, sep = "")) %>%
#   select(FamilyID, RecordID, MEGA.FID, MEGA.IID, MEGA.LabID, Locus_AlleleGroup, EXCLUDE_from_analysis) %>%
#   mutate(Has.Locus_AlleleGroup = 1) %>%
#   group_by(FamilyID, RecordID, MEGA.FID, MEGA.IID, MEGA.LabID, EXCLUDE_from_analysis, Locus_AlleleGroup) %>%
#   summarise(Dosage.Locus_AlleleGroup = sum(Has.Locus_AlleleGroup)) %>%
#   arrange(desc(Dosage.Locus_AlleleGroup)) %>%
#   spread(key = Locus_AlleleGroup, value = Dosage.Locus_AlleleGroup) %>%
#   gather(key = "Locus_AlleleGroup", value = "Dosage.Locus_AlleleGroup", `HLA-A*01`:`HLA-DRB1*16`) %>%
#   mutate(Dosage.Locus_AlleleGroup = ifelse(is.na(Dosage.Locus_AlleleGroup), 0, Dosage.Locus_AlleleGroup))

# analysisData.alleleGroups <- hla.imputed_long %>%
#   select(FamilyID, RecordID, MEGA.FID, MEGA.IID, MEGA.LabID, Locus, Allele) %>%
#   unique() %>%
#   separate(Allele, into = c("AlleleGroup", "Protein"), sep = "[:]", extra = "merge", remove = FALSE) %>%
#   mutate(Locus_AlleleGroup = paste(Locus, AlleleGroup, sep = ""),
#          dummy = 1) %>%
#   spread(key = Locus_AlleleGroup, value = dummy) %>%
#   gather(key = "Locus_AlleleGroup", value = "Dosage", `HLA-A*01`:`HLA-DRB1*16`) %>%
#   mutate(Dosage = ifelse(is.na(Dosage), 0, Dosage))

if (analysisData.alleleGroups %>%
  filter(EXCLUDE_from_analysis == 0) %>%
    select(MEGA.IID, Locus, Locus_AlleleGroup, Dosage.Locus_AlleleGroup) %>%
    group_by(MEGA.IID, Locus) %>%
    summarise(Total_Dosage.Locus = sum(Dosage.Locus_AlleleGroup)) %>%
    arrange(desc(Total_Dosage.Locus)) %>%
    filter(Total_Dosage.Locus != 2) %>%
    nrow() == 0) {
  print("All IIDs have exactly 2 alleles for each locus. Good!")
  } else {
    print("Oops, not all IIDs have exactly 2 alleles for each locus (and they should).")
  }
`summarise()` has grouped output by 'MEGA.IID'. You can override using the `.groups` argument.
[1] "All IIDs have exactly 2 alleles for each locus. Good!"
if (analysisData.alleleGroups %>%
  filter(EXCLUDE_from_analysis == 0) %>%
    select(RecordID, Locus, Locus_AlleleGroup, Dosage.Locus_AlleleGroup) %>%
    group_by(RecordID, Locus) %>%
    summarise(Total_Dosage.Locus = sum(Dosage.Locus_AlleleGroup)) %>%
    arrange(desc(Total_Dosage.Locus)) %>%
    filter(Total_Dosage.Locus != 2) %>%
    nrow() == 0) {
  print("All RecordIDs have exactly 2 alleles for each locus. Good!")
  } else {
    print("Oops, not all RecordIDs have exactly 2 alleles for each locus (and they should).")
  }
`summarise()` has grouped output by 'RecordID'. You can override using the `.groups` argument.
[1] "All RecordIDs have exactly 2 alleles for each locus. Good!"
analysisData.alleleGroups %>%
  split(., .$Locus_AlleleGroup) %>%
  lapply(dim) %>%
  unique()
[[1]]
[1] 221  10
analysisData.alleleGroups

setwd(dir)
Warning: The working directory was changed to /Users/shawjes/Dropbox/EspinosaGroup/ANALYSIS/Celiac_MultiOmics/GRS/DSMIG_Shared/Manuscript_Figure1/Data inside a notebook chunk. The working directory will be reset when the chunk is finished running. Use the knitr root.dir option in the setup chunk to change the working directory for notebook chunks.
fwrite(analysisData.alleleGroups, "MEGA_041822_Imputed_HLA_AlleleGroups_v0.1_JRS.csv")
fwrite(analysisData.alleleGroups, "MEGA_041822_Imputed_HLA_AlleleGroups_v0.1_JRS.tsv", sep = "\t")

Derive dosage of Celiac-associated heterodimers

Pietzak, Michelle M., et al. “Stratifying Risk for Celiac Disease in a Large At-Risk United States Population by Using HLA Alleles.” Clinical Gastroenterology and Hepatology: The Official Clinical Practice Journal of the American Gastroenterological Association, vol. 7, no. 9, Sept. 2009, pp. 966–71. PubMed, https://doi.org/10.1016/j.cgh.2009.05.028. “HLA-DQ2.5 = DQA105-DQB10201 (DR3), HLA-DQ2.2 = DQA10201-DQB10202 (DR7), HLA-DR5 = DQA105-DQB103 , HLA DQ8 = DQA103-DQB10302 .” (https://pubmed.ncbi.nlm.nih.gov/19500688/)

temp <- hla.imputed_long %>%
  select(ID_colnames,
         EXCLUDE_from_analysis, EXCLUDE_reason,
         Locus, Allele) %>%
  filter(Locus=="HLA-DQA1" | Locus=="HLA-DQB1") %>%
  group_by(FamilyID, RecordID, MEGA.FID, MEGA.IID, MEGA.LabID,
           EXCLUDE_from_analysis, EXCLUDE_reason,
           Locus, Allele) %>%
  summarise(Dosage = n()) %>%
  ungroup() %>%
  arrange(Dosage) %>%
  mutate(DQA1_05 = ifelse(Locus=="HLA-DQA1" & grepl("05[:]", Allele)==TRUE, Dosage, 0),
         DQA1_03 = ifelse(Locus=="HLA-DQA1" & grepl("03[:]", Allele)==TRUE, Dosage, 0),
         DQA1_02 = ifelse(Locus=="HLA-DQA1" & grepl("02[:]", Allele)==TRUE, Dosage, 0),
         DQA1_0201 = ifelse(Locus=="HLA-DQA1" & grepl("02[:]01", Allele)==TRUE, Dosage, 0),
         DQB1_02 = ifelse(Locus=="HLA-DQB1" & grepl("02[:]", Allele)==TRUE, Dosage, 0),
         DQB1_0202 = ifelse(Locus=="HLA-DQB1" & grepl("02[:]02", Allele)==TRUE, Dosage, 0),
         DQB1_0302 = ifelse(Locus=="HLA-DQB1" & grepl("03[:]02", Allele)==TRUE, Dosage, 0),
         # DQ7.5= DQA1*05-DQB1*03. Does not matter what the second number (i.e 0501, 0505 both count) (https://pubmed.ncbi.nlm.nih.gov/19500688/)
         DQB1_03 = ifelse(Locus == "HLA-DQB1" & grepl("03[:]", Allele)==TRUE, Dosage, 0),
         DQB1_0301 = ifelse(Locus == "HLA-DQB1" & grepl("03[:]01", Allele)==TRUE, Dosage, 0))
Note: Using an external vector in selections is ambiguous.
ℹ Use `all_of(ID_colnames)` instead of `ID_colnames` to silence this message.
ℹ See <https://tidyselect.r-lib.org/reference/faq-external-vector.html>.
This message is displayed once per session.
`summarise()` has grouped output by 'FamilyID', 'RecordID', 'MEGA.FID', 'MEGA.IID', 'MEGA.LabID', 'EXCLUDE_from_analysis', 'EXCLUDE_reason',
'Locus'. You can override using the `.groups` argument.
temp %>%
  select(MEGA.LabID,
         EXCLUDE_from_analysis, EXCLUDE_reason,
         Locus, Allele, Dosage,
         DQA1_05, DQA1_03, DQA1_0201,
         DQB1_02, DQB1_0202, DQB1_0301, DQB1_0302) %>%
  unique()

temp2 <- temp %>%
  select(-c(Locus, Allele, Dosage)) %>%
  group_by(FamilyID, RecordID, MEGA.FID, MEGA.IID, MEGA.LabID, EXCLUDE_from_analysis, EXCLUDE_reason) %>%
  summarise(DQA1_05 = sum(DQA1_05),
            DQA1_03 = sum(DQA1_03),
            DQA1_02 = sum(DQA1_02),
            DQA1_0201 = sum(DQA1_0201),
            DQB1_02 = sum(DQB1_02),
            DQB1_0202 = sum(DQB1_0202),
            DQB1_0302 = sum(DQB1_0302),
            DQB1_03 = sum(DQB1_03),
            DQB1_0301 = sum(DQB1_0301)) %>%
  ungroup() %>%
  mutate( DQ2.5 = ifelse(DQA1_05>=1 & DQB1_02>=1, 1, 0),
          DQ8 = ifelse(DQA1_03>=1 & DQB1_0302>=1, 1, 0),
          #DQ2.2.archive040521 = ifelse(DQA1_02>=1 & DQB1_02>=1, 1, 0),
          DQ2.2 = ifelse(DQA1_0201 >=1 & DQB1_0202 >=1, 1, 0),
          DQ7.5 = ifelse(DQA1_05>=1 & DQB1_0301>=1, 1, 0) # SharpEtAl DQ7.5 (DQA1*05, DQB1*03:01)
  )
`summarise()` has grouped output by 'FamilyID', 'RecordID', 'MEGA.FID', 'MEGA.IID', 'MEGA.LabID', 'EXCLUDE_from_analysis'. You can override using
the `.groups` argument.
          # 7.5= DQA1*05-DQB1*03. Marisa Stahl: "Does not matter what the second number (i.e 0501, 0505 both count)" (https://pubmed.ncbi.nlm.nih.gov/19500688/)
          # Pietzak: "HLA-DQ2.5  DQA1*05-DQB1*0201 (DR3), HLA-DQ2.2  DQA1*0201-DQB1*0202 (DR7), HLA-DR5  DQA1*05-DQB1*03, HLA DQ8 DQA1*03-DQB1*0302."
          # https://www.ncbi.nlm.nih.gov/pmc/articles/PMC4297300/
          # "The very few patients, who are not DQ2.5, DQ2.2, or DQ8, are almost all DQ7.5 (DQA1*05, DQB1*03:01) (Karell et al. 2003)."
          # Karell K, Louka AS, Moodie SJ, Ascher H, Clot F, Greco L, Ciclitira PJ, Sollid LM, Partanen J. HLA types in celiac disease patients not carrying the DQA1*05-DQB1*02 (DQ2) heterodimer: results from the European genetics cluster on celiac disease. Hum Immunol. 2003;64:469–477. doi: 10.1016/S0198-8859(03)00027-2.
          #DQ7.5_PietzakEtAl = ifelse(DQA1_05>=1 & DQB1_0301>=1, 1, 0),

df.heterodimer_dosage <- temp2 %>% arrange(MEGA.LabID)

print("Note: HTP0073A2 and HTP0374A carried DQ2.2 in 5/2021 version of data cleaning.")
[1] "Note: HTP0073A2 and HTP0374A carried DQ2.2 in 5/2021 version of data cleaning."
# View the dataframe:
df.heterodimer_dosage

Summarize the data in its interim state:

df.heterodimer_dosage %>%
  group_by(DQ2.5, DQ8, DQ2.2, DQ7.5) %>%
  summarise(N = n())
`summarise()` has grouped output by 'DQ2.5', 'DQ8', 'DQ2.2'. You can override using the `.groups` argument.
print("Per Marsia Stahl, \"DQ7.5 with DQ2.2 is equivalent to DQ2.5 'in trans'\"")
[1] "Per Marsia Stahl, \"DQ7.5 with DQ2.2 is equivalent to DQ2.5 'in trans'\""
print("Observation: All participants who have DQ2.2+DQ7.5 (DQ2.5 in trans) also have imputed genotypes for DQ2.5.")
[1] "Observation: All participants who have DQ2.2+DQ7.5 (DQ2.5 in trans) also have imputed genotypes for DQ2.5."
print("Decision: For these participants with DQ2.2+DQ7.5+DQ2.2, consider them DQ2.5 in trans.")
[1] "Decision: For these participants with DQ2.2+DQ7.5+DQ2.2, consider them DQ2.5 in trans."
print("Therefore:")
[1] "Therefore:"
print("If DQ2.5 == 1 & DQ2.2 == 0 & DQ7.5 == 0, 'DQ2.5'")
[1] "If DQ2.5 == 1 & DQ2.2 == 0 & DQ7.5 == 0, 'DQ2.5'"
print("If DQ2.5 == 1 & DQ2.2 == 1 & DQ7.5 == 1, 'DQ2.5 or DQ2.5 in trans'")
[1] "If DQ2.5 == 1 & DQ2.2 == 1 & DQ7.5 == 1, 'DQ2.5 or DQ2.5 in trans'"
df.heterodimer_dosage %>%
  mutate(DQ2.5_notInTrans = ifelse(DQ2.5 == 1 & DQ2.2 == 0 & DQ7.5 == 0, 1, 0),
         DQ2.5_or_DQ2.5inTrans = ifelse(DQ2.5 == 1 & DQ2.2 == 1 & DQ7.5 == 1, 1, 0)) %>%
  select(FamilyID, RecordID, MEGA.FID, MEGA.IID, MEGA.LabID,
         EXCLUDE_from_analysis, EXCLUDE_reason,
         DQ2.5_notInTrans, DQ2.5_or_DQ2.5inTrans) %>%
  unique() %>%
  group_by(DQ2.5_notInTrans, DQ2.5_or_DQ2.5inTrans) %>%
  summarise(N = n())
`summarise()` has grouped output by 'DQ2.5_notInTrans'. You can override using the `.groups` argument.
print("We have 9 samples that are marked as only DQ2.5 but not possibly DQ2.5 in trans. Meanwhile we have 27 that are DQ2.5 in trans but not DQ2.5. Good, this is how the code was supposed to work.")
[1] "We have 9 samples that are marked as only DQ2.5 but not possibly DQ2.5 in trans. Meanwhile we have 27 that are DQ2.5 in trans but not DQ2.5. Good, this is how the code was supposed to work."

Create a variable that reflects the combination of heterodimers carried by each participant

df.Heterodimer_Combo <- df.heterodimer_dosage %>%
  mutate(DQ2.5 = gsub("1", "DQ2.5", DQ2.5),
         DQ2.5 = gsub("0", "", DQ2.5)) %>%
  mutate(DQ8 = gsub("1", "DQ8", DQ8),
         DQ8 = gsub("0", "", DQ8)) %>%
  mutate(DQ7.5 = gsub("1", "DQ7.5", DQ7.5),
         DQ7.5 = gsub("0", "", DQ7.5)) %>%
  mutate(DQ2.2 = gsub("1", "DQ2.2", DQ2.2),
         DQ2.2 = gsub("0", "", DQ2.2)) %>%
  mutate(Heterodimer_Combo = paste(DQ2.5, DQ8, DQ2.2, DQ7.5, sep = "+")) %>%
  mutate(Heterodimer_Combo = gsub("[+][+]", "+", Heterodimer_Combo)) %>%
  mutate(Heterodimer_Combo = gsub("[+][+]", "+", Heterodimer_Combo)) %>%
  select(ID_colnames, EXCLUDE_from_analysis, EXCLUDE_reason,
         Heterodimer_Combo) %>% #, DQ2.5, DQ8, DQ2.2, DQ7.5) %>%
  unique() %>%
  mutate(Heterodimer_Combo = ifelse(Heterodimer_Combo == "+DQ8+", "DQ8", Heterodimer_Combo),
         Heterodimer_Combo = ifelse(Heterodimer_Combo == "+DQ7.5", "DQ7.5", Heterodimer_Combo),
         Heterodimer_Combo = ifelse(Heterodimer_Combo == "DQ2.5+", "DQ2.5", Heterodimer_Combo),
         Heterodimer_Combo = ifelse(Heterodimer_Combo == "+", "", Heterodimer_Combo),
         Heterodimer_Combo = ifelse(Heterodimer_Combo == "+DQ8+DQ2.2+", "DQ8 + DQ2.2", Heterodimer_Combo),
         Heterodimer_Combo = ifelse(Heterodimer_Combo == "+DQ2.2+", "DQ2.2", Heterodimer_Combo),
         Heterodimer_Combo = ifelse(Heterodimer_Combo == "DQ2.5+DQ2.2+", "DQ2.5 + DQ2.2", Heterodimer_Combo),
         Heterodimer_Combo = ifelse(Heterodimer_Combo == "+DQ8+DQ7.5", "DQ8 + DQ7.5", Heterodimer_Combo),
         Heterodimer_Combo = ifelse(Heterodimer_Combo == "DQ2.5+DQ8+", "DQ2.5 + DQ8", Heterodimer_Combo),
         Heterodimer_Combo = ifelse(Heterodimer_Combo == "DQ2.5+DQ2.2+DQ7.5", "DQ2.5 + DQ2.2 + DQ7.5", Heterodimer_Combo),
         Heterodimer_Combo = ifelse(Heterodimer_Combo == "DQ2.5+DQ7.5", "DQ2.5 + DQ7.5", Heterodimer_Combo)) %>%
  left_join(df.heterodimer_dosage, by = c(ID_colnames, "EXCLUDE_from_analysis", "EXCLUDE_reason")) %>%
  #filter(Heterodimer_Combo == "") %>%
  mutate(Heterodimer_Combo = ifelse(Heterodimer_Combo == "", "X + X", Heterodimer_Combo)) %>%
  mutate(Heterodimer_Combo = ifelse(Heterodimer_Combo == "DQ7.5", "DQ7.5 + X",
                                    ifelse(Heterodimer_Combo == "DQ2.5", "DQ2.5 + X",
                                           ifelse(Heterodimer_Combo == "DQ2.2", "DQ2.2 + X",
                                                  ifelse(Heterodimer_Combo == "DQ8", "DQ8 + X",
                                                         Heterodimer_Combo)))))
df.Heterodimer_Combo

df.Heterodimer_Combo %>%
  select(Heterodimer_Combo) %>%
  unique() %>%
  arrange(Heterodimer_Combo)
# DQ2.2 + X             
# DQ2.5 + DQ2.2             
# DQ2.5 + DQ2.2 + DQ7.5             
# DQ2.5 + DQ7.5             
# DQ2.5 + DQ8               
# DQ2.5 + X             
# DQ7.5 + X             
# DQ8 + DQ2.2               
# DQ8 + DQ7.5               
# DQ8 + X
# X + X

Create a variable that represents HLA-DQ Genotype the same way as in the Sharp et al. paper

# Notes:
# Of the 15 HLA-DQ genotypes represented in the Sharp paper, only 11 were observed in the HTP imputed HLA dataset.
# Individuals with imputed heterodimer combination DQ2.5 + DQ2.2 + DQ7.5 were assumed to be carriers of HLA-DQ2.5 in trans, rather than true HLA-DQ2.5 (phasing error).
# Individuals with other combinations involving HLA-DQ2.5 were assumed to have true HLA-DQ2.5.
# Of course, these assumptions may be flawed given that this is imputed HLA data.

# 15 genotypes reported in the Sharp paper (* not observed in HTP):
# *DQ2.5/DQ2.5
# DQ2.5/DQ2.2
# DQ7.5/DQ2.2
# DQ2.5/DQ8
# DQ2.5/X
# DQ7.5/DQ2.5
# *DQ8/DQ8
# DQ2.2/DQ8
# *DQ2.2/DQ2.2
# DQ7.5/DQ8
# DQ8/X
# DQ2.2/X
# DQ7.5/X
# DQ7.5/DQ7.5
# X/X

df.HLA_DQ_Genotype <- df.Heterodimer_Combo %>%
  mutate(HLA_DQ_Genotype = ifelse(Heterodimer_Combo == "DQ2.5 + DQ2.5", "DQ2.5/DQ2.5",
                                  ifelse(Heterodimer_Combo == "DQ2.5 + DQ2.2", "DQ2.5/DQ2.2",
                                         ifelse(Heterodimer_Combo == "DQ2.5 + DQ2.2 + DQ7.5", "DQ7.5/DQ2.2", # This assumption has since been validated for the Celiac cases via molecular HLA typing.
                                                ifelse(Heterodimer_Combo == "DQ2.5 + DQ8", "DQ2.5/DQ8",
                                                       ifelse(Heterodimer_Combo == "DQ2.5 + X", "DQ2.5/X",
                                                              ifelse(Heterodimer_Combo == "DQ2.5 + DQ7.5", "DQ7.5/DQ2.5",
                                                                     ifelse(Heterodimer_Combo == "DQ8 + DQ8", "DQ8/DQ8",
                                                                            ifelse(Heterodimer_Combo == "DQ8 + DQ2.2", "DQ2.2/DQ8",
                                                                                   ifelse(Heterodimer_Combo == "DQ2.2 + .DQ2.2", "DQ2.2/DQ2.2",
                                                                                          ifelse(Heterodimer_Combo == "DQ8 + DQ7.5", "DQ7.5/DQ8",
                                                                                                 ifelse(Heterodimer_Combo == "DQ8 + X", "DQ8/X",
                                                                                                        ifelse(Heterodimer_Combo == "DQ2.2 + X", "DQ2.2/X",
                                                                                                               ifelse(Heterodimer_Combo == "DQ7.5 + X", "DQ7.5/X",
                                                                                                                      ifelse(Heterodimer_Combo == "DQ7.5 + DQ7.5", "DQ7.5/DQ7.5",
                                                                                                                             ifelse(Heterodimer_Combo == "X + X", "X/X",
                                                                                                                                    NA)))))))))))))))) %>%
  rename(Imputed_heterodimers = Heterodimer_Combo) %>%
  select(ID_colnames, EXCLUDE_from_analysis, EXCLUDE_reason,
         DQ2.5, DQ8, DQ2.2, DQ7.5, Imputed_heterodimers, HLA_DQ_Genotype)

#### Print the dataframe:
df.HLA_DQ_Genotype

Output heterodimers_to_HLAgenotype to CSV and TSV

setwd(dir)
Warning: The working directory was changed to /Users/shawjes/Dropbox/EspinosaGroup/ANALYSIS/Celiac_MultiOmics/GRS/DSMIG_Shared/Manuscript_Figure1/Data inside a notebook chunk. The working directory will be reset when the chunk is finished running. Use the knitr root.dir option in the setup chunk to change the working directory for notebook chunks.
fwrite(heterodimers_to_HLAgenotype, "HLA_041822_Imputed_HLA_heterodimers_to_Inferred_HLADQ_Genotype_v0.1_JRS.csv")

Verify that each HTP_ID is only included in this dataframe once after EXCLUDE filter

df.HLA_DQ_Genotype %>%
  filter(EXCLUDE_from_analysis == 0) %>%
  group_by(RecordID) %>%
  summarise(N = n()) %>%
  arrange(desc(N)) %>%
  filter(N > 1)

print("Good.")
[1] "Good."

Verify that each participant has exactly one HLA-DQ genotype, and only one status per allele (0 or 1):

df.HLA_DQ_Genotype %>%
  select(MEGA.IID, HLA_DQ_Genotype) %>%
  group_by(MEGA.IID) %>%
  summarise(N = n()) %>%
  arrange(desc(N)) %>%
  filter(N != 1 | is.na(N))

df.HLA_DQ_Genotype %>%
  select(MEGA.IID, HLA_DQ_Genotype) %>%
  group_by(MEGA.IID, HLA_DQ_Genotype) %>%
  summarise(N = n()) %>%
  arrange(desc(N)) %>%
  filter(N != 1 | is.na(N))
`summarise()` has grouped output by 'MEGA.IID'. You can override using the `.groups` argument.
print("Good, each participant has exactly one HLA-DQ genotype, and only one status per allele (0 or 1).")
[1] "Good, each participant has exactly one HLA-DQ genotype, and only one status per allele (0 or 1)."

Prepare long-format dataset of HLA-DQ genotypes (1 if the person has the genotype, 0 if they have some other genotype)

df.HLA_DQ_Genotype.LONG <- df.HLA_DQ_Genotype %>%
  select(-c(DQ2.5, DQ8, DQ2.2, DQ7.5, Imputed_heterodimers)) %>%
  unique() %>%
  mutate(dummy = 1) %>%
  spread(key = HLA_DQ_Genotype, value = dummy) %>%
  gather(key = HLA_DQ_Genotype, value = Dosage, `DQ2.2/DQ8`:`X/X`) %>%
  mutate(Dosage = ifelse(is.na(Dosage), 0, Dosage))

df.HLA_DQ_Genotype.LONG

Prepare long-format dataset of HLA-DQ genotypes (1 if the person has the genotype, 0 if they have some other genotype)

temp1 <- df.HLA_DQ_Genotype %>%
  select(ID_colnames, EXCLUDE_from_analysis, EXCLUDE_reason, HLA_DQ_Genotype) %>%
  separate(HLA_DQ_Genotype, into = c("Heterodimer1", "Heterodimer2"), sep = "/", extra = "merge", remove = TRUE) %>%
  select(ID_colnames, EXCLUDE_from_analysis, EXCLUDE_reason, Heterodimer1) %>%
  unique() %>%
  mutate(Heterodimer_Index = 1) %>%
  rename(Heterodimer = Heterodimer1)

temp2 <- df.HLA_DQ_Genotype %>%
  select(ID_colnames, EXCLUDE_from_analysis, EXCLUDE_reason, HLA_DQ_Genotype) %>%
  separate(HLA_DQ_Genotype, into = c("Heterodimer1", "Heterodimer2"), sep = "/", extra = "merge", remove = TRUE) %>%
  select(ID_colnames, EXCLUDE_from_analysis, EXCLUDE_reason, Heterodimer2) %>%
  unique() %>%
  mutate(Heterodimer_Index = 2) %>%
  rename(Heterodimer = Heterodimer2)

df.HLA_heterodimers.LONG <- rbind(temp1, temp2) %>%
  group_by(FamilyID, RecordID, MEGA.FID,
           MEGA.IID, MEGA.LabID,
           EXCLUDE_from_analysis, EXCLUDE_reason, Heterodimer) %>%
  summarise(Dosage = n()) %>%
  spread(key = Heterodimer, value = Dosage) %>%
  gather(DQ2.2:X, key = Heterodimer, value = Dosage) %>%
  mutate(Dosage = ifelse(is.na(Dosage), 0, Dosage))
`summarise()` has grouped output by 'FamilyID', 'RecordID', 'MEGA.FID', 'MEGA.IID', 'MEGA.LabID', 'EXCLUDE_from_analysis', 'EXCLUDE_reason'. You can override using
the `.groups` argument.
df.HLA_heterodimers.LONG

Remove the exclusion columns; we will add them back once the analysis metadata is finalized

df.HLA_DQ_Genotype01 <- df.HLA_DQ_Genotype %>%
  select(-c(EXCLUDE_from_analysis, EXCLUDE_reason))

df.HLA_DQ_Genotype.LONG01 <- df.HLA_DQ_Genotype.LONG %>%
  select(-c(EXCLUDE_from_analysis, EXCLUDE_reason))

df.HLA_DQ_Genotype01

df.HLA_DQ_Genotype.LONG01

Output datasets to CSV and TSV

setwd(dir)
Warning: The working directory was changed to /Users/shawjes/Dropbox/EspinosaGroup/ANALYSIS/Celiac_MultiOmics/GRS/DSMIG_Shared/Manuscript_Figure1/Data inside a notebook chunk. The working directory will be reset when the chunk is finished running. Use the knitr root.dir option in the setup chunk to change the working directory for notebook chunks.
fwrite(df.HLA_DQ_Genotype01, "HLA_041822_HLA_Dosage_Heterodimers_DQgenotypes_SharpDQ7.5_v0.1_JRS.csv")
fwrite(df.HLA_DQ_Genotype01, "HLA_041822_HLA_Dosage_Heterodimers_DQgenotypes_SharpDQ7.5_v0.1_JRS.tsv", sep = "\t")

setwd(dir)
fwrite(df.HLA_DQ_Genotype.LONG01, "HLA_041822_HLA_Genotype_Dosage_LONG_v0.1_JRS.csv")
fwrite(df.HLA_DQ_Genotype.LONG01, "HLA_041822_HLA_Genotype_Dosage_LONG_v0.1_JRS.tsv", sep = "\t")

setwd(dir)
fwrite(df.HLA_DQ_Genotype.LONG01, "HLA_041822_HLA_Heterodimers_Dosage_LONG_v0.1_JRS.csv")
fwrite(df.HLA_DQ_Genotype.LONG01, "HLA_041822_HLA_Heterodimers_Dosage_LONG_v0.1_JRS.tsv", sep = "\t")
LS0tCnRpdGxlOiAiUiBOb3RlYm9vayIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKIyMjIyBMb2FkIGZyZXF1ZW50bHkgdXNlZCBwYWNrYWdlcwpgYGB7cn0KbGlicmFyeShvcGVueGxzeCkKbGlicmFyeShnZ3JlcGVsKQpsaWJyYXJ5KGRwbHlyKQpsaWJyYXJ5KHRpZHlyKQpsaWJyYXJ5KGRhdGEudGFibGUpCmxpYnJhcnkoYnJvb20pCmxpYnJhcnkoYnJvb21FeHRyYSkKbGlicmFyeSh0aWJibGUpCmxpYnJhcnkoc2pzdGF0cykKbGlicmFyeShjYXIpCmxpYnJhcnkobG1lNCkKbGlicmFyeShsbWVyVGVzdCkKbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KHRpYmJsZSkKbGlicmFyeShtb2RlbHIpCmxpYnJhcnkodGlkeXZlcnNlKQojbGlicmFyeShtaWNlYWRkcykKbGlicmFyeShnZ2ZvcmNlKQpyZXF1aXJlKG9wZW54bHN4KQpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeShjYXJldCkKbGlicmFyeShnbG1uZXQpCmxpYnJhcnkoZ2dwbG90MikKc2VsZWN0IDwtIGRwbHlyOjpzZWxlY3QKZmlsdGVyIDwtIGRwbHlyOjpmaWx0ZXIKYGBgCgojIyMjIFByaW50IHNlc3Npb24gaW5mbyBmb3IgcmVwcm9kdWNpYmlsaXR5CmBgYHtyfQpzYXZlLnNlc3Npb25JbmZvIDwtIHNlc3Npb25JbmZvKCkKCnNhdmUuc2Vzc2lvbkluZm8KYGBgCgojIyMjIERlZmluZSBpbi9vdXQgZGlyZWN0b3J5IHVzZWQgaW4gdGhpcyBzY3JpcHQKYGBge3J9CmRpciA8LSAiL1VzZXJzL3NoYXdqZXMvRHJvcGJveC9Fc3Bpbm9zYUdyb3VwL0FOQUxZU0lTL0NlbGlhY19NdWx0aU9taWNzL0dSUy9EU01JR19TaGFyZWQvTWFudXNjcmlwdF9GaWd1cmUxL0RhdGEiCmRpci5Bbm5vLkdSU29yaWdpbmFsIDwtICIvVXNlcnMvc2hhd2plcy9Ecm9wYm94L0VzcGlub3NhR3JvdXAvQU5BTFlTSVMvQ2VsaWFjX011bHRpT21pY3MvR1JTL0RTTUlHX1NoYXJlZC9NYW51c2NyaXB0X0ZpZ3VyZTEvQW5ub3RhdGlvbi9HUlNvcmlnaW5hbCIKZGlyLkFubm8uR1JTcmV2aXNlZCA8LSAiL1VzZXJzL3NoYXdqZXMvRHJvcGJveC9Fc3Bpbm9zYUdyb3VwL0FOQUxZU0lTL0NlbGlhY19NdWx0aU9taWNzL0dSUy9EU01JR19TaGFyZWQvTWFudXNjcmlwdF9GaWd1cmUxL0Fubm90YXRpb24vR1JTb3JpZ2luYWwiCmBgYAoKIyMjIyBMb2FkIE1FR0EgdG8gSFRQIElEIGtleQpgYGB7cn0Kc2V0d2QoZGlyKQpNRUdBLklEa2V5IDwtIGZyZWFkKCJNRUdBXzA0MTgyMl9NRUdBMl90b19IVFBfSURfa2V5X3YwLjFfSlJTLmNzdiIpCgpNRUdBLklEa2V5CmBgYAoKIyMjIyBQdXQgdGhlIGNvbHVtbiBuYW1lcyByZXByZXNlbnRpbmcgaWRlbnRpZmllcnMgaW50byBhIHZlY3RvciBmb3IgcmVmZXJlbmNlIGJlbG93CmBgYHtyfQpJRF9jb2xuYW1lcyA8LSBjKCJGYW1pbHlJRCIsICJSZWNvcmRJRCIsICJNRUdBLkZJRCIsICJNRUdBLklJRCIsICJNRUdBLkxhYklEIikKYGBgCgojIyMjIExvYWQgdGhlIGFuYWx5c2lzIG1ldGFkYXRhCmBgYHtyfQpzZXR3ZChkaXIpCm1ldGEuTUVHQS5UMjFfdmlzaXQxX0NlbGlhYyA8LSBmcmVhZCgiTUVHQV8wNDE4MjJfTUVUQV9DZWxpYWNHUlNfdjAuMV9KUlMuY3N2IikKCm1ldGEuTUVHQS5UMjFfdmlzaXQxX0NlbGlhYwpgYGAKCiMjIyMjIEZvciByZWZlcmVuY2UgbGF0ZXIsIHJlYWQgaW4gc3VwcGxlbWVudGFsIHRhYmxlcyBmcm9tIFNoYXJwIGV0IGFsLiwgMjAxOQpgYGB7cn0Kc2V0d2QoZGlyLkFubm8uR1JTb3JpZ2luYWwpClRhYmxlX1MyIDwtIHJlYWQueGxzeCgiYXB0MTU4MjYtc3VwLTAwMDEtc3VwaW5mby54bHN4Iiwgc2hlZXQgPSAiVGFibGUgUzIiLCBzdGFydFJvdyA9IDMpICU+JQogIHNlcGFyYXRlKGBPZGRzLlJhdGlvLls5NSUuQ0ldYCwgaW50byA9IGMoIk9SIiwgIkNJIiksIHNlcCA9ICIgIiwgZXh0cmEgPSAibWVyZ2UiLCByZW1vdmUgPSBUUlVFKSAlPiUKICByZW5hbWUoT1IuR1JTID0gT1IsCiAgICAgICAgIENJLkdSUyA9IENJLAogICAgICAgICBXZWlnaHQuR1JTID0gYFdlaWdodC4ozrIpYCkKVGFibGVfUzIKCnNldHdkKGRpci5Bbm5vLkdSU29yaWdpbmFsKQpUYWJsZV9TMyA8LSByZWFkLnhsc3goImFwdDE1ODI2LXN1cC0wMDAxLXN1cGluZm8ueGxzeCIsIHNoZWV0ID0gIlRhYmxlIFMzIiwgc3RhcnRSb3cgPSAzKSAlPiUKICByZW5hbWUoT1IuR1JTID0gT1IsCiAgICAgICAgIFdlaWdodC5HUlMgPSBgV2VpZ2h0LijOsilgKQpUYWJsZV9TMwpgYGAKCiMjIyMgUmVhZCBpbiBpbXB1dGVkIEhMQSBnZW5vdHlwZSBkYXRhIHByb3ZpZGVkIGJ5IFBhdWwgTm9ybWFuCmBgYHtyfQpzZXR3ZChkaXIuR1JTZGF0YSkKaGxhLmltcHV0ZWQgPC0gb3Blbnhsc3g6OnJlYWQueGxzeCgiRXNwaW5vc2FITEFzdHVkeV9ITEFjYWxsc19QSk4ueGxzeCIpCgojaGxhLmltcHV0ZWQgJT4lIGNvbG5hbWVzKCkKY29sbmFtZXMoaGxhLmltcHV0ZWQpPC1jKCJNRUdBLkxhYklEIiwKICAgICAgICAgICAgICAgICAgICAgICAgICJSYWNlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICJFdGhuaWNpdHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgInNhbXBsZS5pZCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAiSExBLUEuMSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAiSExBLUEuMiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAiSExBLUIuMSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAiSExBLUIuMiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAiSExBLUMuMSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAiSExBLUMuMiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAiSExBLURSQjEuMSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAiSExBLURSQjEuMiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAiSExBLURRQTEuMSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAiSExBLURRQTEuMiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAiSExBLURRQjEuMSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAiSExBLURRQjEuMiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAiSExBLURQQjEuMSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAiSExBLURQQjEuMiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAiQ29tbWVudHMiKQoKaGxhLmltcHV0ZWQKCiMgQ2hlY2sgdGhhdCBNRUdBLkxhYklEID09IHNhbXBsZS5pZApobGEuaW1wdXRlZCAlPiUgZmlsdGVyKE1FR0EuTGFiSUQhPXNhbXBsZS5pZCB8IGlzLm5hKE1FR0EuTGFiSUQpIHwgaXMubmEoc2FtcGxlLmlkKSkKIyBHb29kCmBgYAoKIyMjIyBKb2luIHdpdGggdGhlIE1FR0EgdG8gSFRQIElEIGtleSBhbmQgcHJlcGFyZWQgYW5hbHlzaXMgbWV0YWRhdGEKYGBge3J9CmhsYS5pbXB1dGVkMDEgPC0gaGxhLmltcHV0ZWQgJT4lCiAgIyBSZW1vdmUgc2FtcGxlLmlkIHNpbmNlIGl0IGlzIHJlZHVuZGFudCwgYXMgc2hvd24gYWJvdmUuCiAgc2VsZWN0KC1jKHNhbXBsZS5pZCkpICU+JQogICMgUmVtb3ZlIG1ldGFkYXRhIGNvbHVtbnMgaW4gdGhlIGRhdGEgd2UgZ290IGJhY2sgZnJvbSBOb3JtYW4ncyBncm91cDsgdG8gYmUgcmVwbGFjZWQgYXQgdGltZSBvZiBhbmFseXNpcyB3aXRoIGxhdGVzdCB2ZXJzaW9uIG9mIG1ldGFkYXRhLgogIHNlbGVjdCgtYyhSYWNlLCBFdGhuaWNpdHkpKSAlPiUKICBsZWZ0X2pvaW4oTUVHQS5JRGtleSwgYnkgPSAiTUVHQS5MYWJJRCIpICU+JQogIHNlbGVjdChjb2xuYW1lcyhNRUdBLklEa2V5KSwgZXZlcnl0aGluZygpKSAlPiUKICBzZWxlY3QoLWMoSFRQX3BhcnRpY2lwYW50LCBJbl9wbGlua19mYW0pKSAlPiUKICBmdWxsX2pvaW4obWV0YS5NRUdBLlQyMV92aXNpdDFfQ2VsaWFjLCBieSA9IElEX2NvbG5hbWVzKQoKIyBWaWV3IHRoZSBkYXRhIGJlZm9yZSBmdXJ0aGVyIG1hbmlwdWxhdGlvbjoKaGxhLmltcHV0ZWQwMQoKcm0oaGxhLmltcHV0ZWQpOyBnYygpCgpwcmludCgiTm90ZTogSSBhbSBwdXJwb3NlbHkgY2FsbGluZyB0aGUgTGFiSUQgY29sdW1uIE1FR0EuTGFiSUQgYmVjYXVzZSBpdCBpcyBhbiBpZGVudGlmaWVyIGxpbmtlZCB0byBzdGF0aWMgZGF0YS4gVGhlIGdlbmV0aWMgZGF0YSBzaG91bGRuJ3QgdmFyeSBieSB3aGljaCBzYW1wbGUgd2FzIHVzZWQgZm9yIGdlbm90eXBpbmcgKGlmIGl0IGRvZXMsIHRoYXQncyBtb3N0IGxpa2VseSBhIHF1YWxpdHkgY29udHJvbCBwcm9ibGVtKS4gSW4gYW5hbHlzZXMgdXNpbmcgdGhlIE1FR0EgZ2VuZXRpYyBkYXRhLCB3ZSB3aWxsIGxpa2VseSB3YW50IHRvIGpvaW4gdGhlIGdlbmV0aWMgZGF0YXNldHMgd2l0aCBtZXRhZGF0YSBieSB0aGUgTGFiSUQgcmVsZXZhbnQgdG8gdGhlIG90aGVyIGtleSB2YXJpYWJsZXMgaW4gdGhlIGFuYWx5c2lzLiBGb3IgZXhhbXBsZSwgaW4gdGhpcyBwcm9qZWN0IHdlIGFyZSBsb29raW5nIGF0IENlbGlhYyBzdGF0dXMgYXQgdmlzaXQgMSwgc28gd2hlbiB3ZSBhZGQgaW4gb3VyIG1ldGFkYXRhIHdlIHdpbGwgcHJvYmFibHkgd2FudCB0byB1c2UgYWdlIGF0IHZpc2l0IDEgaW5zdGVhZCBvZiBhZ2UgYXQgdGltZSBvZiB0aGUgc2FtcGxlIHVzZWQgZm9yIGdlbm90eXBpbmcuIikKYGBgCgojIyMjIENvbnZlcnQgSExBIGdlbm90eXBlIGRhdGFzZXQgaW50byBsb25nIGZvcm1hdApgYGB7cn0KaGxhLmltcHV0ZWRfbG9uZyA8LSBobGEuaW1wdXRlZDAxICU+JQogIGdhdGhlcihrZXk9IkxvY3VzIiwgdmFsdWU9IkFsbGVsZSIsIGBITEEtQS4xYDpgSExBLURQQjEuMmApICU+JQogIG11dGF0ZShMb2N1cyA9IGdzdWIoIlsuXTEiLCAiIiwgTG9jdXMpLAogICAgICAgICBMb2N1cyA9IGdzdWIoIlsuXTIiLCAiIiwgTG9jdXMpKSAlPiUKICBzZWxlY3QoRmFtaWx5SUQsIFJlY29yZElELCBNRUdBLkZJRCwgTUVHQS5JSUQsIE1FR0EuTGFiSUQsCiAgICAgICAgIExvY3VzLCBBbGxlbGUsIENvbW1lbnRzLCBldmVyeXRoaW5nKCkpCgpobGEuaW1wdXRlZF9sb25nCmBgYAoKIyMjIyBWZXJpZnkgdGhhdCBlYWNoIHBhcnRpY2lwYW50IGhhcyBleGFjdGx5IHR3byBITEEgYWxsZWxlcyBmb3IgZWFjaCBsb2N1cwpgYGB7cn0KaWYgKGhsYS5pbXB1dGVkX2xvbmcgJT4lCiAgZmlsdGVyKEVYQ0xVREVfZnJvbV9hbmFseXNpcyA9PSAwKSAlPiUKICAgIHNlbGVjdChNRUdBLklJRCwgTG9jdXMsIEFsbGVsZSkgJT4lCiAgICBncm91cF9ieShNRUdBLklJRCwgTG9jdXMpICU+JQogICAgc3VtbWFyaXNlKE49bigpKSAlPiUKICAgIGFycmFuZ2UoZGVzYyhOKSkgJT4lCiAgICBmaWx0ZXIoTiE9MikgJT4lCiAgICBucm93KCkgPT0gMCkgewogIHByaW50KCJBbGwgSUlEcyBoYXZlIGV4YWN0bHkgMiBhbGxlbGVzIGZvciBlYWNoIGxvY3VzLiBHb29kISIpCiAgfSBlbHNlIHsKICAgIHByaW50KCJPb3BzLCBub3QgYWxsIElJRHMgaGF2ZSBleGFjdGx5IDIgYWxsZWxlcyBmb3IgZWFjaCBsb2N1cyAoYW5kIHRoZXkgc2hvdWxkKS4iKQogIH0KCmlmIChobGEuaW1wdXRlZF9sb25nICU+JQogIGZpbHRlcihFWENMVURFX2Zyb21fYW5hbHlzaXMgPT0gMCkgJT4lCiAgICBzZWxlY3QoUmVjb3JkSUQsIExvY3VzLCBBbGxlbGUpICU+JQogICAgZ3JvdXBfYnkoUmVjb3JkSUQsIExvY3VzKSAlPiUKICAgIHN1bW1hcmlzZShOPW4oKSkgJT4lCiAgICBhcnJhbmdlKGRlc2MoTikpICU+JQogICAgZmlsdGVyKE4hPTIpICU+JQogICAgbnJvdygpID09IDApIHsKICBwcmludCgiQWxsIFJlY29yZElEcyBoYXZlIGV4YWN0bHkgMiBhbGxlbGVzIGZvciBlYWNoIGxvY3VzLiBHb29kISIpCiAgfSBlbHNlIHsKICAgIHByaW50KCJPb3BzLCBub3QgYWxsIFJlY29yZElEcyBoYXZlIGV4YWN0bHkgMiBhbGxlbGVzIGZvciBlYWNoIGxvY3VzIChhbmQgdGhleSBzaG91bGQpLiIpCiAgfQpgYGAKCiMjIyMgRG91YmxlIGNoZWNrIHRoYXQgdGhlIEVYQ0xVREUgZmlsdGVyIHByb2R1Y2VzIGEgZGF0YXNldCB0aGF0IGluY2x1ZGVzIG9ubHkgb25lIHNldCBvZiBITEEgdHlwaW5nIHBlciBSZWNvcmRJRApgYGB7cn0KaWYgKCBobGEuaW1wdXRlZF9sb25nICU+JQogICAgICAgZmlsdGVyKEVYQ0xVREVfZnJvbV9hbmFseXNpcyA9PSAwKSAlPiUKICAgICBzZWxlY3QoUmVjb3JkSUQsIE1FR0EuTGFiSUQpICU+JQogICAgIHVuaXF1ZSgpICU+JQogICAgIGdyb3VwX2J5KFJlY29yZElEKSAlPiUKICAgICBzdW1tYXJpc2UoTl9MYWJJRHMgPSBuKCkpICU+JQogICAgIGZpbHRlcihOX0xhYklEcyA+IDEpICU+JQogICAgIG5yb3coKSA9PSAwICkgewogIHByaW50KCJBbGwgUmVjb3JkSURzIGhhdmUgb25seSBvbmUgTGFiSUQuIEdvb2QhIikKICB9IGVsc2UgewogICAgcHJpbnQoIk9vcHMsIG5vdCBhbGwgUmVjb3JkSURzIGhhdmUgb25seSBvbmUgTGFiSUQuIikKICAgIH0KYGBgCgojIyMjIFZpZXcgdGhlIGxvbmcgZm9ybWF0IGRhdGFzZXQgc28gZmFyCmBgYHtyfQpobGEuaW1wdXRlZF9sb25nCmBgYAoKIyMjIyBNYWtlIGEgZGF0YXNldCBvZiB0aGUgSExBLURRIGFsbGVsZXMgYW5kIG91dHB1dCB0byBDU1YgYW5kIFRTVgpgYGB7cn0KYW5hbHlzaXNEYXRhLmFsbGVsZXMgPC0gaGxhLmltcHV0ZWRfbG9uZyAlPiUKICBzZWxlY3QoRmFtaWx5SUQsIFJlY29yZElELCBNRUdBLkZJRCwgTUVHQS5JSUQsIE1FR0EuTGFiSUQsIExvY3VzLCBBbGxlbGUsIEVYQ0xVREVfZnJvbV9hbmFseXNpcykgJT4lCiAgbXV0YXRlKExvY3VzX0FsbGVsZSA9IHBhc3RlKExvY3VzLCBBbGxlbGUsIHNlcCA9ICIiKSkgJT4lCiAgc2VsZWN0KC1jKEFsbGVsZSkpICU+JQogIG11dGF0ZShIYXMuTG9jdXNfQWxsZWxlID0gMSkgJT4lCiAgZ3JvdXBfYnkoRmFtaWx5SUQsIFJlY29yZElELCBNRUdBLkZJRCwgTUVHQS5JSUQsIE1FR0EuTGFiSUQsIEVYQ0xVREVfZnJvbV9hbmFseXNpcywgTG9jdXNfQWxsZWxlKSAlPiUKICBzdW1tYXJpc2UoRG9zYWdlLkxvY3VzX0FsbGVsZSA9IHN1bShIYXMuTG9jdXNfQWxsZWxlKSkgJT4lCiAgdW5ncm91cCgpICU+JQogIGFycmFuZ2UoZGVzYyhEb3NhZ2UuTG9jdXNfQWxsZWxlKSkgJT4lCiAgc3ByZWFkKGtleSA9IExvY3VzX0FsbGVsZSwgdmFsdWUgPSBEb3NhZ2UuTG9jdXNfQWxsZWxlKSAlPiUKICBnYXRoZXIoa2V5ID0gIkxvY3VzX0FsbGVsZSIsIHZhbHVlID0gIkRvc2FnZS5Mb2N1c19BbGxlbGUiLCBgSExBLUEqMDE6MDFgOmBITEEtRFJCMSoxNjowMmApICU+JQogIG11dGF0ZShEb3NhZ2UuTG9jdXNfQWxsZWxlID0gaWZlbHNlKGlzLm5hKERvc2FnZS5Mb2N1c19BbGxlbGUpLCAwLCBEb3NhZ2UuTG9jdXNfQWxsZWxlKSkgJT4lCiAgc2VwYXJhdGUoTG9jdXNfQWxsZWxlLCBpbnRvID0gYygiTG9jdXMiLCAiQWxsZWxlIiksIHNlcCA9ICJbKl0iLCBleHRyYSA9ICJtZXJnZSIsIHJlbW92ZSA9IEZBTFNFKSAlPiUKICBzZWxlY3QoRmFtaWx5SUQsIFJlY29yZElELCBNRUdBLkZJRCwgTUVHQS5JSUQsIE1FR0EuTGFiSUQsIExvY3VzLCBMb2N1c19BbGxlbGUsIERvc2FnZS5Mb2N1c19BbGxlbGUsIEVYQ0xVREVfZnJvbV9hbmFseXNpcywgZXZlcnl0aGluZygpKQoKYW5hbHlzaXNEYXRhLmFsbGVsZXMKCmlmIChhbmFseXNpc0RhdGEuYWxsZWxlcyAlPiUKICBmaWx0ZXIoRVhDTFVERV9mcm9tX2FuYWx5c2lzID09IDApICU+JQogICAgc2VsZWN0KE1FR0EuSUlELCBMb2N1cywgTG9jdXNfQWxsZWxlLCBEb3NhZ2UuTG9jdXNfQWxsZWxlKSAlPiUKICAgIGdyb3VwX2J5KE1FR0EuSUlELCBMb2N1cykgJT4lCiAgICBzdW1tYXJpc2UoVG90YWxfRG9zYWdlLkxvY3VzID0gc3VtKERvc2FnZS5Mb2N1c19BbGxlbGUpKSAlPiUKICAgIGFycmFuZ2UoZGVzYyhUb3RhbF9Eb3NhZ2UuTG9jdXMpKSAlPiUKICAgIGZpbHRlcihUb3RhbF9Eb3NhZ2UuTG9jdXMgIT0gMikgJT4lCiAgICBucm93KCkgPT0gMCkgewogIHByaW50KCJBbGwgSUlEcyBoYXZlIGV4YWN0bHkgMiBhbGxlbGVzIGZvciBlYWNoIGxvY3VzLiBHb29kISIpCiAgfSBlbHNlIHsKICAgIHByaW50KCJPb3BzLCBub3QgYWxsIElJRHMgaGF2ZSBleGFjdGx5IDIgYWxsZWxlcyBmb3IgZWFjaCBsb2N1cyAoYW5kIHRoZXkgc2hvdWxkKS4iKQogIH0KCmlmIChhbmFseXNpc0RhdGEuYWxsZWxlcyAlPiUKICBmaWx0ZXIoRVhDTFVERV9mcm9tX2FuYWx5c2lzID09IDApICU+JQogICAgc2VsZWN0KFJlY29yZElELCBMb2N1cywgTG9jdXNfQWxsZWxlLCBEb3NhZ2UuTG9jdXNfQWxsZWxlKSAlPiUKICAgIGdyb3VwX2J5KFJlY29yZElELCBMb2N1cykgJT4lCiAgICBzdW1tYXJpc2UoVG90YWxfRG9zYWdlLkxvY3VzID0gc3VtKERvc2FnZS5Mb2N1c19BbGxlbGUpKSAlPiUKICAgIGFycmFuZ2UoZGVzYyhUb3RhbF9Eb3NhZ2UuTG9jdXMpKSAlPiUKICAgIGZpbHRlcihUb3RhbF9Eb3NhZ2UuTG9jdXMgIT0gMikgJT4lCiAgICBucm93KCkgPT0gMCkgewogIHByaW50KCJBbGwgUmVjb3JkSURzIGhhdmUgZXhhY3RseSAyIGFsbGVsZXMgZm9yIGVhY2ggbG9jdXMuIEdvb2QhIikKICB9IGVsc2UgewogICAgcHJpbnQoIk9vcHMsIG5vdCBhbGwgUmVjb3JkSURzIGhhdmUgZXhhY3RseSAyIGFsbGVsZXMgZm9yIGVhY2ggbG9jdXMgKGFuZCB0aGV5IHNob3VsZCkuIikKICB9CgphbmFseXNpc0RhdGEuYWxsZWxlcyAlPiUKICBzcGxpdCguLCAuJExvY3VzX0FsbGVsZSkgJT4lCiAgbGFwcGx5KGRpbSkgJT4lCiAgdW5pcXVlKCkKICAKYW5hbHlzaXNEYXRhLmFsbGVsZXMKCnNldHdkKGRpcikKZndyaXRlKGFuYWx5c2lzRGF0YS5hbGxlbGVzLCAiTUVHQV8wNDE4MjJfSW1wdXRlZF9ITEFfQWxsZWxlc192MC4xX0pSUy5jc3YiKQpmd3JpdGUoYW5hbHlzaXNEYXRhLmFsbGVsZXMsICJNRUdBXzA0MTgyMl9JbXB1dGVkX0hMQV9BbGxlbGVzX3YwLjFfSlJTLnRzdiIsIHNlcCA9ICJcdCIpCmBgYAoKIyMjIyBNYWtlIGEgZGF0YXNldCBvZiB0aGUgSExBLURRIGFsbGVsZSBncm91cHMgYW5kIG91dHB1dCB0byBDU1YgYW5kIFRTVgpgYGB7cn0KYW5hbHlzaXNEYXRhLmFsbGVsZUdyb3VwcyA8LSBobGEuaW1wdXRlZF9sb25nICU+JQogIHNlbGVjdChGYW1pbHlJRCwgUmVjb3JkSUQsIE1FR0EuRklELCBNRUdBLklJRCwgTUVHQS5MYWJJRCwgTG9jdXMsIEFsbGVsZSwgRVhDTFVERV9mcm9tX2FuYWx5c2lzKSAlPiUKICBzZXBhcmF0ZShBbGxlbGUsIGludG8gPSBjKCJBbGxlbGVHcm91cCIsICJQcm90ZWluIiksIHNlcCA9ICJbOl0iLCBleHRyYSA9ICJtZXJnZSIsIHJlbW92ZSA9IEZBTFNFKSAlPiUKICBtdXRhdGUoTG9jdXNfQWxsZWxlR3JvdXAgPSBwYXN0ZShMb2N1cywgQWxsZWxlR3JvdXAsIHNlcCA9ICIiKSkgJT4lCiAgbXV0YXRlKEhhcy5Mb2N1c19BbGxlbGVHcm91cCA9IDEpICU+JQogIHNlbGVjdChGYW1pbHlJRCwgUmVjb3JkSUQsIE1FR0EuRklELCBNRUdBLklJRCwgTUVHQS5MYWJJRCwgTG9jdXNfQWxsZWxlR3JvdXAsIEhhcy5Mb2N1c19BbGxlbGVHcm91cCwgRVhDTFVERV9mcm9tX2FuYWx5c2lzKSAlPiUKICBncm91cF9ieShGYW1pbHlJRCwgUmVjb3JkSUQsIE1FR0EuRklELCBNRUdBLklJRCwgTUVHQS5MYWJJRCwgRVhDTFVERV9mcm9tX2FuYWx5c2lzLCBMb2N1c19BbGxlbGVHcm91cCApICU+JQogIHN1bW1hcmlzZShEb3NhZ2UuTG9jdXNfQWxsZWxlR3JvdXAgPSBzdW0oSGFzLkxvY3VzX0FsbGVsZUdyb3VwKSkgJT4lCiAgdW5ncm91cCgpICU+JQogIGFycmFuZ2UoZGVzYyhEb3NhZ2UuTG9jdXNfQWxsZWxlR3JvdXApKSAlPiUKICBhcnJhbmdlKERvc2FnZS5Mb2N1c19BbGxlbGVHcm91cCkgJT4lCiAgc3ByZWFkKGtleSA9IExvY3VzX0FsbGVsZUdyb3VwLCB2YWx1ZSA9IERvc2FnZS5Mb2N1c19BbGxlbGVHcm91cCkgJT4lCiAgZ2F0aGVyKGtleSA9ICJMb2N1c19BbGxlbGVHcm91cCIsIHZhbHVlID0gIkRvc2FnZS5Mb2N1c19BbGxlbGVHcm91cCIsIGBITEEtQSowMWA6YEhMQS1EUkIxKjE2YCkgJT4lCiAgbXV0YXRlKERvc2FnZS5Mb2N1c19BbGxlbGVHcm91cCA9IGlmZWxzZShpcy5uYShEb3NhZ2UuTG9jdXNfQWxsZWxlR3JvdXApLCAwLCBEb3NhZ2UuTG9jdXNfQWxsZWxlR3JvdXApKSAlPiUKICBzZXBhcmF0ZShMb2N1c19BbGxlbGVHcm91cCwgaW50byA9IGMoIkxvY3VzIiwgIkFsbGVsZUdyb3VwIiksIHNlcCA9ICJbKl0iLCBleHRyYSA9ICJtZXJnZSIsIHJlbW92ZSA9IEZBTFNFKSAlPiUKICBzZWxlY3QoRmFtaWx5SUQsIFJlY29yZElELCBNRUdBLkZJRCwgTUVHQS5JSUQsIE1FR0EuTGFiSUQsIExvY3VzLCBMb2N1c19BbGxlbGVHcm91cCwgRG9zYWdlLkxvY3VzX0FsbGVsZUdyb3VwLCBFWENMVURFX2Zyb21fYW5hbHlzaXMsIGV2ZXJ5dGhpbmcoKSkKCiMgYW5hbHlzaXNEYXRhLmFsbGVsZUdyb3VwcyA8LSBobGEuaW1wdXRlZF9sb25nICU+JQojICAgc2VsZWN0KEZhbWlseUlELCBSZWNvcmRJRCwgTUVHQS5GSUQsIE1FR0EuSUlELCBNRUdBLkxhYklELCBMb2N1cywgQWxsZWxlLCBFWENMVURFX2Zyb21fYW5hbHlzaXMpICU+JQojICAgc2VwYXJhdGUoQWxsZWxlLCBpbnRvID0gYygiQWxsZWxlR3JvdXAiLCAiUHJvdGVpbiIpLCBzZXAgPSAiWzpdIiwgZXh0cmEgPSAibWVyZ2UiLCByZW1vdmUgPSBGQUxTRSkgJT4lCiMgICBtdXRhdGUoTG9jdXNfQWxsZWxlR3JvdXAgPSBwYXN0ZShMb2N1cywgQWxsZWxlR3JvdXAsIHNlcCA9ICIiKSkgJT4lCiMgICBzZWxlY3QoRmFtaWx5SUQsIFJlY29yZElELCBNRUdBLkZJRCwgTUVHQS5JSUQsIE1FR0EuTGFiSUQsIExvY3VzX0FsbGVsZUdyb3VwLCBFWENMVURFX2Zyb21fYW5hbHlzaXMpICU+JQojICAgbXV0YXRlKEhhcy5Mb2N1c19BbGxlbGVHcm91cCA9IDEpICU+JQojICAgZ3JvdXBfYnkoRmFtaWx5SUQsIFJlY29yZElELCBNRUdBLkZJRCwgTUVHQS5JSUQsIE1FR0EuTGFiSUQsIEVYQ0xVREVfZnJvbV9hbmFseXNpcywgTG9jdXNfQWxsZWxlR3JvdXApICU+JQojICAgc3VtbWFyaXNlKERvc2FnZS5Mb2N1c19BbGxlbGVHcm91cCA9IHN1bShIYXMuTG9jdXNfQWxsZWxlR3JvdXApKSAlPiUKIyAgIGFycmFuZ2UoZGVzYyhEb3NhZ2UuTG9jdXNfQWxsZWxlR3JvdXApKSAlPiUKIyAgIHNwcmVhZChrZXkgPSBMb2N1c19BbGxlbGVHcm91cCwgdmFsdWUgPSBEb3NhZ2UuTG9jdXNfQWxsZWxlR3JvdXApICU+JQojICAgZ2F0aGVyKGtleSA9ICJMb2N1c19BbGxlbGVHcm91cCIsIHZhbHVlID0gIkRvc2FnZS5Mb2N1c19BbGxlbGVHcm91cCIsIGBITEEtQSowMWA6YEhMQS1EUkIxKjE2YCkgJT4lCiMgICBtdXRhdGUoRG9zYWdlLkxvY3VzX0FsbGVsZUdyb3VwID0gaWZlbHNlKGlzLm5hKERvc2FnZS5Mb2N1c19BbGxlbGVHcm91cCksIDAsIERvc2FnZS5Mb2N1c19BbGxlbGVHcm91cCkpCgojIGFuYWx5c2lzRGF0YS5hbGxlbGVHcm91cHMgPC0gaGxhLmltcHV0ZWRfbG9uZyAlPiUKIyAgIHNlbGVjdChGYW1pbHlJRCwgUmVjb3JkSUQsIE1FR0EuRklELCBNRUdBLklJRCwgTUVHQS5MYWJJRCwgTG9jdXMsIEFsbGVsZSkgJT4lCiMgICB1bmlxdWUoKSAlPiUKIyAgIHNlcGFyYXRlKEFsbGVsZSwgaW50byA9IGMoIkFsbGVsZUdyb3VwIiwgIlByb3RlaW4iKSwgc2VwID0gIls6XSIsIGV4dHJhID0gIm1lcmdlIiwgcmVtb3ZlID0gRkFMU0UpICU+JQojICAgbXV0YXRlKExvY3VzX0FsbGVsZUdyb3VwID0gcGFzdGUoTG9jdXMsIEFsbGVsZUdyb3VwLCBzZXAgPSAiIiksCiMgICAgICAgICAgZHVtbXkgPSAxKSAlPiUKIyAgIHNwcmVhZChrZXkgPSBMb2N1c19BbGxlbGVHcm91cCwgdmFsdWUgPSBkdW1teSkgJT4lCiMgICBnYXRoZXIoa2V5ID0gIkxvY3VzX0FsbGVsZUdyb3VwIiwgdmFsdWUgPSAiRG9zYWdlIiwgYEhMQS1BKjAxYDpgSExBLURSQjEqMTZgKSAlPiUKIyAgIG11dGF0ZShEb3NhZ2UgPSBpZmVsc2UoaXMubmEoRG9zYWdlKSwgMCwgRG9zYWdlKSkKCmlmIChhbmFseXNpc0RhdGEuYWxsZWxlR3JvdXBzICU+JQogIGZpbHRlcihFWENMVURFX2Zyb21fYW5hbHlzaXMgPT0gMCkgJT4lCiAgICBzZWxlY3QoTUVHQS5JSUQsIExvY3VzLCBMb2N1c19BbGxlbGVHcm91cCwgRG9zYWdlLkxvY3VzX0FsbGVsZUdyb3VwKSAlPiUKICAgIGdyb3VwX2J5KE1FR0EuSUlELCBMb2N1cykgJT4lCiAgICBzdW1tYXJpc2UoVG90YWxfRG9zYWdlLkxvY3VzID0gc3VtKERvc2FnZS5Mb2N1c19BbGxlbGVHcm91cCkpICU+JQogICAgYXJyYW5nZShkZXNjKFRvdGFsX0Rvc2FnZS5Mb2N1cykpICU+JQogICAgZmlsdGVyKFRvdGFsX0Rvc2FnZS5Mb2N1cyAhPSAyKSAlPiUKICAgIG5yb3coKSA9PSAwKSB7CiAgcHJpbnQoIkFsbCBJSURzIGhhdmUgZXhhY3RseSAyIGFsbGVsZXMgZm9yIGVhY2ggbG9jdXMuIEdvb2QhIikKICB9IGVsc2UgewogICAgcHJpbnQoIk9vcHMsIG5vdCBhbGwgSUlEcyBoYXZlIGV4YWN0bHkgMiBhbGxlbGVzIGZvciBlYWNoIGxvY3VzIChhbmQgdGhleSBzaG91bGQpLiIpCiAgfQoKaWYgKGFuYWx5c2lzRGF0YS5hbGxlbGVHcm91cHMgJT4lCiAgZmlsdGVyKEVYQ0xVREVfZnJvbV9hbmFseXNpcyA9PSAwKSAlPiUKICAgIHNlbGVjdChSZWNvcmRJRCwgTG9jdXMsIExvY3VzX0FsbGVsZUdyb3VwLCBEb3NhZ2UuTG9jdXNfQWxsZWxlR3JvdXApICU+JQogICAgZ3JvdXBfYnkoUmVjb3JkSUQsIExvY3VzKSAlPiUKICAgIHN1bW1hcmlzZShUb3RhbF9Eb3NhZ2UuTG9jdXMgPSBzdW0oRG9zYWdlLkxvY3VzX0FsbGVsZUdyb3VwKSkgJT4lCiAgICBhcnJhbmdlKGRlc2MoVG90YWxfRG9zYWdlLkxvY3VzKSkgJT4lCiAgICBmaWx0ZXIoVG90YWxfRG9zYWdlLkxvY3VzICE9IDIpICU+JQogICAgbnJvdygpID09IDApIHsKICBwcmludCgiQWxsIFJlY29yZElEcyBoYXZlIGV4YWN0bHkgMiBhbGxlbGVzIGZvciBlYWNoIGxvY3VzLiBHb29kISIpCiAgfSBlbHNlIHsKICAgIHByaW50KCJPb3BzLCBub3QgYWxsIFJlY29yZElEcyBoYXZlIGV4YWN0bHkgMiBhbGxlbGVzIGZvciBlYWNoIGxvY3VzIChhbmQgdGhleSBzaG91bGQpLiIpCiAgfQoKYW5hbHlzaXNEYXRhLmFsbGVsZUdyb3VwcyAlPiUKICBzcGxpdCguLCAuJExvY3VzX0FsbGVsZUdyb3VwKSAlPiUKICBsYXBwbHkoZGltKSAlPiUKICB1bmlxdWUoKQoKYW5hbHlzaXNEYXRhLmFsbGVsZUdyb3VwcwoKc2V0d2QoZGlyKQpmd3JpdGUoYW5hbHlzaXNEYXRhLmFsbGVsZUdyb3VwcywgIk1FR0FfMDQxODIyX0ltcHV0ZWRfSExBX0FsbGVsZUdyb3Vwc192MC4xX0pSUy5jc3YiKQpmd3JpdGUoYW5hbHlzaXNEYXRhLmFsbGVsZUdyb3VwcywgIk1FR0FfMDQxODIyX0ltcHV0ZWRfSExBX0FsbGVsZUdyb3Vwc192MC4xX0pSUy50c3YiLCBzZXAgPSAiXHQiKQpgYGAKCiMjIyMgRGVyaXZlIGRvc2FnZSBvZiBDZWxpYWMtYXNzb2NpYXRlZCBoZXRlcm9kaW1lcnMKUGlldHphaywgTWljaGVsbGUgTS4sIGV0IGFsLiDigJxTdHJhdGlmeWluZyBSaXNrIGZvciBDZWxpYWMgRGlzZWFzZSBpbiBhIExhcmdlIEF0LVJpc2sgVW5pdGVkIFN0YXRlcyBQb3B1bGF0aW9uIGJ5IFVzaW5nIEhMQSBBbGxlbGVzLuKAnSBDbGluaWNhbCBHYXN0cm9lbnRlcm9sb2d5IGFuZCBIZXBhdG9sb2d5OiBUaGUgT2ZmaWNpYWwgQ2xpbmljYWwgUHJhY3RpY2UgSm91cm5hbCBvZiB0aGUgQW1lcmljYW4gR2FzdHJvZW50ZXJvbG9naWNhbCBBc3NvY2lhdGlvbiwgdm9sLiA3LCBuby4gOSwgU2VwdC4gMjAwOSwgcHAuIDk2NuKAkzcxLiBQdWJNZWQsIGh0dHBzOi8vZG9pLm9yZy8xMC4xMDE2L2ouY2doLjIwMDkuMDUuMDI4LgoiSExBLURRMi41ID0gRFFBMSowNS1EUUIxKjAyMDEgKERSMyksCiBITEEtRFEyLjIgPSBEUUExKjAyMDEtRFFCMSowMjAyIChEUjcpLAogSExBLURSNSA9IERRQTEqMDUtRFFCMSowMyAsCiBITEEgRFE4ID0gRFFBMSowMy1EUUIxKjAzMDIgLiIKKGh0dHBzOi8vcHVibWVkLm5jYmkubmxtLm5paC5nb3YvMTk1MDA2ODgvKQpgYGB7cn0KdGVtcCA8LSBobGEuaW1wdXRlZF9sb25nICU+JQogIHNlbGVjdChJRF9jb2xuYW1lcywKICAgICAgICAgRVhDTFVERV9mcm9tX2FuYWx5c2lzLCBFWENMVURFX3JlYXNvbiwKICAgICAgICAgTG9jdXMsIEFsbGVsZSkgJT4lCiAgZmlsdGVyKExvY3VzPT0iSExBLURRQTEiIHwgTG9jdXM9PSJITEEtRFFCMSIpICU+JQogIGdyb3VwX2J5KEZhbWlseUlELCBSZWNvcmRJRCwgTUVHQS5GSUQsIE1FR0EuSUlELCBNRUdBLkxhYklELAogICAgICAgICAgIEVYQ0xVREVfZnJvbV9hbmFseXNpcywgRVhDTFVERV9yZWFzb24sCiAgICAgICAgICAgTG9jdXMsIEFsbGVsZSkgJT4lCiAgc3VtbWFyaXNlKERvc2FnZSA9IG4oKSkgJT4lCiAgdW5ncm91cCgpICU+JQogIGFycmFuZ2UoRG9zYWdlKSAlPiUKICBtdXRhdGUoRFFBMV8wNSA9IGlmZWxzZShMb2N1cz09IkhMQS1EUUExIiAmIGdyZXBsKCIwNVs6XSIsIEFsbGVsZSk9PVRSVUUsIERvc2FnZSwgMCksCiAgICAgICAgIERRQTFfMDMgPSBpZmVsc2UoTG9jdXM9PSJITEEtRFFBMSIgJiBncmVwbCgiMDNbOl0iLCBBbGxlbGUpPT1UUlVFLCBEb3NhZ2UsIDApLAogICAgICAgICBEUUExXzAyID0gaWZlbHNlKExvY3VzPT0iSExBLURRQTEiICYgZ3JlcGwoIjAyWzpdIiwgQWxsZWxlKT09VFJVRSwgRG9zYWdlLCAwKSwKICAgICAgICAgRFFBMV8wMjAxID0gaWZlbHNlKExvY3VzPT0iSExBLURRQTEiICYgZ3JlcGwoIjAyWzpdMDEiLCBBbGxlbGUpPT1UUlVFLCBEb3NhZ2UsIDApLAogICAgICAgICBEUUIxXzAyID0gaWZlbHNlKExvY3VzPT0iSExBLURRQjEiICYgZ3JlcGwoIjAyWzpdIiwgQWxsZWxlKT09VFJVRSwgRG9zYWdlLCAwKSwKICAgICAgICAgRFFCMV8wMjAyID0gaWZlbHNlKExvY3VzPT0iSExBLURRQjEiICYgZ3JlcGwoIjAyWzpdMDIiLCBBbGxlbGUpPT1UUlVFLCBEb3NhZ2UsIDApLAogICAgICAgICBEUUIxXzAzMDIgPSBpZmVsc2UoTG9jdXM9PSJITEEtRFFCMSIgJiBncmVwbCgiMDNbOl0wMiIsIEFsbGVsZSk9PVRSVUUsIERvc2FnZSwgMCksCiAgICAgICAgICMgRFE3LjU9IERRQTEqMDUtRFFCMSowMy4gRG9lcyBub3QgbWF0dGVyIHdoYXQgdGhlIHNlY29uZCBudW1iZXIgKGkuZSAwNTAxLCAwNTA1IGJvdGggY291bnQpIChodHRwczovL3B1Ym1lZC5uY2JpLm5sbS5uaWguZ292LzE5NTAwNjg4LykKICAgICAgICAgRFFCMV8wMyA9IGlmZWxzZShMb2N1cyA9PSAiSExBLURRQjEiICYgZ3JlcGwoIjAzWzpdIiwgQWxsZWxlKT09VFJVRSwgRG9zYWdlLCAwKSwKICAgICAgICAgRFFCMV8wMzAxID0gaWZlbHNlKExvY3VzID09ICJITEEtRFFCMSIgJiBncmVwbCgiMDNbOl0wMSIsIEFsbGVsZSk9PVRSVUUsIERvc2FnZSwgMCkpCnRlbXAgJT4lCiAgc2VsZWN0KE1FR0EuTGFiSUQsCiAgICAgICAgIEVYQ0xVREVfZnJvbV9hbmFseXNpcywgRVhDTFVERV9yZWFzb24sCiAgICAgICAgIExvY3VzLCBBbGxlbGUsIERvc2FnZSwKICAgICAgICAgRFFBMV8wNSwgRFFBMV8wMywgRFFBMV8wMjAxLAogICAgICAgICBEUUIxXzAyLCBEUUIxXzAyMDIsIERRQjFfMDMwMSwgRFFCMV8wMzAyKSAlPiUKICB1bmlxdWUoKQoKdGVtcDIgPC0gdGVtcCAlPiUKICBzZWxlY3QoLWMoTG9jdXMsIEFsbGVsZSwgRG9zYWdlKSkgJT4lCiAgZ3JvdXBfYnkoRmFtaWx5SUQsIFJlY29yZElELCBNRUdBLkZJRCwgTUVHQS5JSUQsIE1FR0EuTGFiSUQsIEVYQ0xVREVfZnJvbV9hbmFseXNpcywgRVhDTFVERV9yZWFzb24pICU+JQogIHN1bW1hcmlzZShEUUExXzA1ID0gc3VtKERRQTFfMDUpLAogICAgICAgICAgICBEUUExXzAzID0gc3VtKERRQTFfMDMpLAogICAgICAgICAgICBEUUExXzAyID0gc3VtKERRQTFfMDIpLAogICAgICAgICAgICBEUUExXzAyMDEgPSBzdW0oRFFBMV8wMjAxKSwKICAgICAgICAgICAgRFFCMV8wMiA9IHN1bShEUUIxXzAyKSwKICAgICAgICAgICAgRFFCMV8wMjAyID0gc3VtKERRQjFfMDIwMiksCiAgICAgICAgICAgIERRQjFfMDMwMiA9IHN1bShEUUIxXzAzMDIpLAogICAgICAgICAgICBEUUIxXzAzID0gc3VtKERRQjFfMDMpLAogICAgICAgICAgICBEUUIxXzAzMDEgPSBzdW0oRFFCMV8wMzAxKSkgJT4lCiAgdW5ncm91cCgpICU+JQogIG11dGF0ZSggRFEyLjUgPSBpZmVsc2UoRFFBMV8wNT49MSAmIERRQjFfMDI+PTEsIDEsIDApLAogICAgICAgICAgRFE4ID0gaWZlbHNlKERRQTFfMDM+PTEgJiBEUUIxXzAzMDI+PTEsIDEsIDApLAogICAgICAgICAgI0RRMi4yLmFyY2hpdmUwNDA1MjEgPSBpZmVsc2UoRFFBMV8wMj49MSAmIERRQjFfMDI+PTEsIDEsIDApLAogICAgICAgICAgRFEyLjIgPSBpZmVsc2UoRFFBMV8wMjAxID49MSAmIERRQjFfMDIwMiA+PTEsIDEsIDApLAogICAgICAgICAgRFE3LjUgPSBpZmVsc2UoRFFBMV8wNT49MSAmIERRQjFfMDMwMT49MSwgMSwgMCkgIyBTaGFycEV0QWwgRFE3LjUgKERRQTEqMDUsIERRQjEqMDM6MDEpCiAgKQogICAgICAgICAgIyA3LjU9IERRQTEqMDUtRFFCMSowMy4gTWFyaXNhIFN0YWhsOiAiRG9lcyBub3QgbWF0dGVyIHdoYXQgdGhlIHNlY29uZCBudW1iZXIgKGkuZSAwNTAxLCAwNTA1IGJvdGggY291bnQpIiAoaHR0cHM6Ly9wdWJtZWQubmNiaS5ubG0ubmloLmdvdi8xOTUwMDY4OC8pCiAgICAgICAgICAjIFBpZXR6YWs6ICJITEEtRFEyLjUgIERRQTEqMDUtRFFCMSowMjAxIChEUjMpLCBITEEtRFEyLjIgIERRQTEqMDIwMS1EUUIxKjAyMDIgKERSNyksIEhMQS1EUjUgIERRQTEqMDUtRFFCMSowMywgSExBIERROCBEUUExKjAzLURRQjEqMDMwMi4iCiAgICAgICAgICAjIGh0dHBzOi8vd3d3Lm5jYmkubmxtLm5paC5nb3YvcG1jL2FydGljbGVzL1BNQzQyOTczMDAvCiAgICAgICAgICAjICJUaGUgdmVyeSBmZXcgcGF0aWVudHMsIHdobyBhcmUgbm90IERRMi41LCBEUTIuMiwgb3IgRFE4LCBhcmUgYWxtb3N0IGFsbCBEUTcuNSAoRFFBMSowNSwgRFFCMSowMzowMSkgKEthcmVsbCBldCBhbC4gMjAwMykuIgogICAgICAgICAgIyBLYXJlbGwgSywgTG91a2EgQVMsIE1vb2RpZSBTSiwgQXNjaGVyIEgsIENsb3QgRiwgR3JlY28gTCwgQ2ljbGl0aXJhIFBKLCBTb2xsaWQgTE0sIFBhcnRhbmVuIEouIEhMQSB0eXBlcyBpbiBjZWxpYWMgZGlzZWFzZSBwYXRpZW50cyBub3QgY2FycnlpbmcgdGhlIERRQTEqMDUtRFFCMSowMiAoRFEyKSBoZXRlcm9kaW1lcjogcmVzdWx0cyBmcm9tIHRoZSBFdXJvcGVhbiBnZW5ldGljcyBjbHVzdGVyIG9uIGNlbGlhYyBkaXNlYXNlLiBIdW0gSW1tdW5vbC4gMjAwMzs2NDo0NjnigJM0NzcuIGRvaTogMTAuMTAxNi9TMDE5OC04ODU5KDAzKTAwMDI3LTIuCiAgICAgICAgICAjRFE3LjVfUGlldHpha0V0QWwgPSBpZmVsc2UoRFFBMV8wNT49MSAmIERRQjFfMDMwMT49MSwgMSwgMCksCgpkZi5oZXRlcm9kaW1lcl9kb3NhZ2UgPC0gdGVtcDIgJT4lIGFycmFuZ2UoTUVHQS5MYWJJRCkKCnByaW50KCJOb3RlOiBIVFAwMDczQTIgYW5kIEhUUDAzNzRBIGNhcnJpZWQgRFEyLjIgaW4gNS8yMDIxIHZlcnNpb24gb2YgZGF0YSBjbGVhbmluZy4iKQoKIyBWaWV3IHRoZSBkYXRhZnJhbWU6CmRmLmhldGVyb2RpbWVyX2Rvc2FnZQpgYGAKCiMjIyMgU3VtbWFyaXplIHRoZSBkYXRhIGluIGl0cyBpbnRlcmltIHN0YXRlOgpgYGB7cn0KZGYuaGV0ZXJvZGltZXJfZG9zYWdlICU+JQogIGdyb3VwX2J5KERRMi41LCBEUTgsIERRMi4yLCBEUTcuNSkgJT4lCiAgc3VtbWFyaXNlKE4gPSBuKCkpCnByaW50KCJQZXIgTWFyc2lhIFN0YWhsLCBcIkRRNy41IHdpdGggRFEyLjIgaXMgZXF1aXZhbGVudCB0byBEUTIuNSAnaW4gdHJhbnMnXCIiKQpwcmludCgiT2JzZXJ2YXRpb246IEFsbCBwYXJ0aWNpcGFudHMgd2hvIGhhdmUgRFEyLjIrRFE3LjUgKERRMi41IGluIHRyYW5zKSBhbHNvIGhhdmUgaW1wdXRlZCBnZW5vdHlwZXMgZm9yIERRMi41LiIpCnByaW50KCJEZWNpc2lvbjogRm9yIHRoZXNlIHBhcnRpY2lwYW50cyB3aXRoIERRMi4yK0RRNy41K0RRMi4yLCBjb25zaWRlciB0aGVtIERRMi41IGluIHRyYW5zLiIpCgpwcmludCgiVGhlcmVmb3JlOiIpCnByaW50KCJJZiBEUTIuNSA9PSAxICYgRFEyLjIgPT0gMCAmIERRNy41ID09IDAsICdEUTIuNSciKQpwcmludCgiSWYgRFEyLjUgPT0gMSAmIERRMi4yID09IDEgJiBEUTcuNSA9PSAxLCAnRFEyLjUgb3IgRFEyLjUgaW4gdHJhbnMnIikKCmRmLmhldGVyb2RpbWVyX2Rvc2FnZSAlPiUKICBtdXRhdGUoRFEyLjVfbm90SW5UcmFucyA9IGlmZWxzZShEUTIuNSA9PSAxICYgRFEyLjIgPT0gMCAmIERRNy41ID09IDAsIDEsIDApLAogICAgICAgICBEUTIuNV9vcl9EUTIuNWluVHJhbnMgPSBpZmVsc2UoRFEyLjUgPT0gMSAmIERRMi4yID09IDEgJiBEUTcuNSA9PSAxLCAxLCAwKSkgJT4lCiAgc2VsZWN0KEZhbWlseUlELCBSZWNvcmRJRCwgTUVHQS5GSUQsIE1FR0EuSUlELCBNRUdBLkxhYklELAogICAgICAgICBFWENMVURFX2Zyb21fYW5hbHlzaXMsIEVYQ0xVREVfcmVhc29uLAogICAgICAgICBEUTIuNV9ub3RJblRyYW5zLCBEUTIuNV9vcl9EUTIuNWluVHJhbnMpICU+JQogIHVuaXF1ZSgpICU+JQogIGdyb3VwX2J5KERRMi41X25vdEluVHJhbnMsIERRMi41X29yX0RRMi41aW5UcmFucykgJT4lCiAgc3VtbWFyaXNlKE4gPSBuKCkpCnByaW50KCJXZSBoYXZlIDkgc2FtcGxlcyB0aGF0IGFyZSBtYXJrZWQgYXMgb25seSBEUTIuNSBidXQgbm90IHBvc3NpYmx5IERRMi41IGluIHRyYW5zLiBNZWFud2hpbGUgd2UgaGF2ZSAyNyB0aGF0IGFyZSBEUTIuNSBpbiB0cmFucyBidXQgbm90IERRMi41LiBHb29kLCB0aGlzIGlzIGhvdyB0aGUgY29kZSB3YXMgc3VwcG9zZWQgdG8gd29yay4iKQpgYGAKCiMjIyMgQ3JlYXRlIGEgdmFyaWFibGUgdGhhdCByZWZsZWN0cyB0aGUgY29tYmluYXRpb24gb2YgaGV0ZXJvZGltZXJzIGNhcnJpZWQgYnkgZWFjaCBwYXJ0aWNpcGFudApgYGB7cn0KZGYuSGV0ZXJvZGltZXJfQ29tYm8gPC0gZGYuaGV0ZXJvZGltZXJfZG9zYWdlICU+JQogIG11dGF0ZShEUTIuNSA9IGdzdWIoIjEiLCAiRFEyLjUiLCBEUTIuNSksCiAgICAgICAgIERRMi41ID0gZ3N1YigiMCIsICIiLCBEUTIuNSkpICU+JQogIG11dGF0ZShEUTggPSBnc3ViKCIxIiwgIkRROCIsIERROCksCiAgICAgICAgIERROCA9IGdzdWIoIjAiLCAiIiwgRFE4KSkgJT4lCiAgbXV0YXRlKERRNy41ID0gZ3N1YigiMSIsICJEUTcuNSIsIERRNy41KSwKICAgICAgICAgRFE3LjUgPSBnc3ViKCIwIiwgIiIsIERRNy41KSkgJT4lCiAgbXV0YXRlKERRMi4yID0gZ3N1YigiMSIsICJEUTIuMiIsIERRMi4yKSwKICAgICAgICAgRFEyLjIgPSBnc3ViKCIwIiwgIiIsIERRMi4yKSkgJT4lCiAgbXV0YXRlKEhldGVyb2RpbWVyX0NvbWJvID0gcGFzdGUoRFEyLjUsIERROCwgRFEyLjIsIERRNy41LCBzZXAgPSAiKyIpKSAlPiUKICBtdXRhdGUoSGV0ZXJvZGltZXJfQ29tYm8gPSBnc3ViKCJbK11bK10iLCAiKyIsIEhldGVyb2RpbWVyX0NvbWJvKSkgJT4lCiAgbXV0YXRlKEhldGVyb2RpbWVyX0NvbWJvID0gZ3N1YigiWytdWytdIiwgIisiLCBIZXRlcm9kaW1lcl9Db21ibykpICU+JQogIHNlbGVjdChJRF9jb2xuYW1lcywgRVhDTFVERV9mcm9tX2FuYWx5c2lzLCBFWENMVURFX3JlYXNvbiwKICAgICAgICAgSGV0ZXJvZGltZXJfQ29tYm8pICU+JSAjLCBEUTIuNSwgRFE4LCBEUTIuMiwgRFE3LjUpICU+JQogIHVuaXF1ZSgpICU+JQogIG11dGF0ZShIZXRlcm9kaW1lcl9Db21ibyA9IGlmZWxzZShIZXRlcm9kaW1lcl9Db21ibyA9PSAiK0RROCsiLCAiRFE4IiwgSGV0ZXJvZGltZXJfQ29tYm8pLAogICAgICAgICBIZXRlcm9kaW1lcl9Db21ibyA9IGlmZWxzZShIZXRlcm9kaW1lcl9Db21ibyA9PSAiK0RRNy41IiwgIkRRNy41IiwgSGV0ZXJvZGltZXJfQ29tYm8pLAogICAgICAgICBIZXRlcm9kaW1lcl9Db21ibyA9IGlmZWxzZShIZXRlcm9kaW1lcl9Db21ibyA9PSAiRFEyLjUrIiwgIkRRMi41IiwgSGV0ZXJvZGltZXJfQ29tYm8pLAogICAgICAgICBIZXRlcm9kaW1lcl9Db21ibyA9IGlmZWxzZShIZXRlcm9kaW1lcl9Db21ibyA9PSAiKyIsICIiLCBIZXRlcm9kaW1lcl9Db21ibyksCiAgICAgICAgIEhldGVyb2RpbWVyX0NvbWJvID0gaWZlbHNlKEhldGVyb2RpbWVyX0NvbWJvID09ICIrRFE4K0RRMi4yKyIsICJEUTggKyBEUTIuMiIsIEhldGVyb2RpbWVyX0NvbWJvKSwKICAgICAgICAgSGV0ZXJvZGltZXJfQ29tYm8gPSBpZmVsc2UoSGV0ZXJvZGltZXJfQ29tYm8gPT0gIitEUTIuMisiLCAiRFEyLjIiLCBIZXRlcm9kaW1lcl9Db21ibyksCiAgICAgICAgIEhldGVyb2RpbWVyX0NvbWJvID0gaWZlbHNlKEhldGVyb2RpbWVyX0NvbWJvID09ICJEUTIuNStEUTIuMisiLCAiRFEyLjUgKyBEUTIuMiIsIEhldGVyb2RpbWVyX0NvbWJvKSwKICAgICAgICAgSGV0ZXJvZGltZXJfQ29tYm8gPSBpZmVsc2UoSGV0ZXJvZGltZXJfQ29tYm8gPT0gIitEUTgrRFE3LjUiLCAiRFE4ICsgRFE3LjUiLCBIZXRlcm9kaW1lcl9Db21ibyksCiAgICAgICAgIEhldGVyb2RpbWVyX0NvbWJvID0gaWZlbHNlKEhldGVyb2RpbWVyX0NvbWJvID09ICJEUTIuNStEUTgrIiwgIkRRMi41ICsgRFE4IiwgSGV0ZXJvZGltZXJfQ29tYm8pLAogICAgICAgICBIZXRlcm9kaW1lcl9Db21ibyA9IGlmZWxzZShIZXRlcm9kaW1lcl9Db21ibyA9PSAiRFEyLjUrRFEyLjIrRFE3LjUiLCAiRFEyLjUgKyBEUTIuMiArIERRNy41IiwgSGV0ZXJvZGltZXJfQ29tYm8pLAogICAgICAgICBIZXRlcm9kaW1lcl9Db21ibyA9IGlmZWxzZShIZXRlcm9kaW1lcl9Db21ibyA9PSAiRFEyLjUrRFE3LjUiLCAiRFEyLjUgKyBEUTcuNSIsIEhldGVyb2RpbWVyX0NvbWJvKSkgJT4lCiAgbGVmdF9qb2luKGRmLmhldGVyb2RpbWVyX2Rvc2FnZSwgYnkgPSBjKElEX2NvbG5hbWVzLCAiRVhDTFVERV9mcm9tX2FuYWx5c2lzIiwgIkVYQ0xVREVfcmVhc29uIikpICU+JQogICNmaWx0ZXIoSGV0ZXJvZGltZXJfQ29tYm8gPT0gIiIpICU+JQogIG11dGF0ZShIZXRlcm9kaW1lcl9Db21ibyA9IGlmZWxzZShIZXRlcm9kaW1lcl9Db21ibyA9PSAiIiwgIlggKyBYIiwgSGV0ZXJvZGltZXJfQ29tYm8pKSAlPiUKICBtdXRhdGUoSGV0ZXJvZGltZXJfQ29tYm8gPSBpZmVsc2UoSGV0ZXJvZGltZXJfQ29tYm8gPT0gIkRRNy41IiwgIkRRNy41ICsgWCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmZWxzZShIZXRlcm9kaW1lcl9Db21ibyA9PSAiRFEyLjUiLCAiRFEyLjUgKyBYIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmZWxzZShIZXRlcm9kaW1lcl9Db21ibyA9PSAiRFEyLjIiLCAiRFEyLjIgKyBYIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZmVsc2UoSGV0ZXJvZGltZXJfQ29tYm8gPT0gIkRROCIsICJEUTggKyBYIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgSGV0ZXJvZGltZXJfQ29tYm8pKSkpKQpkZi5IZXRlcm9kaW1lcl9Db21ibwoKZGYuSGV0ZXJvZGltZXJfQ29tYm8gJT4lCiAgc2VsZWN0KEhldGVyb2RpbWVyX0NvbWJvKSAlPiUKICB1bmlxdWUoKSAlPiUKICBhcnJhbmdlKEhldGVyb2RpbWVyX0NvbWJvKQojIERRMi4yICsgWAkJCQkKIyBEUTIuNSArIERRMi4yCQkJCQojIERRMi41ICsgRFEyLjIgKyBEUTcuNQkJCQkKIyBEUTIuNSArIERRNy41CQkJCQojIERRMi41ICsgRFE4CQkJCQojIERRMi41ICsgWAkJCQkKIyBEUTcuNSArIFgJCQkJCiMgRFE4ICsgRFEyLjIJCQkJCiMgRFE4ICsgRFE3LjUJCQkJCiMgRFE4ICsgWAojIFggKyBYCmBgYAoKIyMjIyBDcmVhdGUgYSB2YXJpYWJsZSB0aGF0IHJlcHJlc2VudHMgSExBLURRIEdlbm90eXBlIHRoZSBzYW1lIHdheSBhcyBpbiB0aGUgU2hhcnAgZXQgYWwuIHBhcGVyCmBgYHtyfQojIE5vdGVzOgojIE9mIHRoZSAxNSBITEEtRFEgZ2Vub3R5cGVzIHJlcHJlc2VudGVkIGluIHRoZSBTaGFycCBwYXBlciwgb25seSAxMSB3ZXJlIG9ic2VydmVkIGluIHRoZSBIVFAgaW1wdXRlZCBITEEgZGF0YXNldC4KIyBJbmRpdmlkdWFscyB3aXRoIGltcHV0ZWQgaGV0ZXJvZGltZXIgY29tYmluYXRpb24gRFEyLjUgKyBEUTIuMiArIERRNy41IHdlcmUgYXNzdW1lZCB0byBiZSBjYXJyaWVycyBvZiBITEEtRFEyLjUgaW4gdHJhbnMsIHJhdGhlciB0aGFuIHRydWUgSExBLURRMi41IChwaGFzaW5nIGVycm9yKS4KIyBJbmRpdmlkdWFscyB3aXRoIG90aGVyIGNvbWJpbmF0aW9ucyBpbnZvbHZpbmcgSExBLURRMi41IHdlcmUgYXNzdW1lZCB0byBoYXZlIHRydWUgSExBLURRMi41LgojIE9mIGNvdXJzZSwgdGhlc2UgYXNzdW1wdGlvbnMgbWF5IGJlIGZsYXdlZCBnaXZlbiB0aGF0IHRoaXMgaXMgaW1wdXRlZCBITEEgZGF0YS4KCiMgMTUgZ2Vub3R5cGVzIHJlcG9ydGVkIGluIHRoZSBTaGFycCBwYXBlciAoKiBub3Qgb2JzZXJ2ZWQgaW4gSFRQKToKIyAqRFEyLjUvRFEyLjUKIyBEUTIuNS9EUTIuMgojIERRNy41L0RRMi4yCiMgRFEyLjUvRFE4CiMgRFEyLjUvWAojIERRNy41L0RRMi41CiMgKkRROC9EUTgKIyBEUTIuMi9EUTgKIyAqRFEyLjIvRFEyLjIKIyBEUTcuNS9EUTgKIyBEUTgvWAojIERRMi4yL1gKIyBEUTcuNS9YCiMgRFE3LjUvRFE3LjUKIyBYL1gKCmRmLkhMQV9EUV9HZW5vdHlwZSA8LSBkZi5IZXRlcm9kaW1lcl9Db21ibyAlPiUKICBtdXRhdGUoSExBX0RRX0dlbm90eXBlID0gaWZlbHNlKEhldGVyb2RpbWVyX0NvbWJvID09ICJEUTIuNSArIERRMi41IiwgIkRRMi41L0RRMi41IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmZWxzZShIZXRlcm9kaW1lcl9Db21ibyA9PSAiRFEyLjUgKyBEUTIuMiIsICJEUTIuNS9EUTIuMiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWZlbHNlKEhldGVyb2RpbWVyX0NvbWJvID09ICJEUTIuNSArIERRMi4yICsgRFE3LjUiLCAiRFE3LjUvRFEyLjIiLCAjIFRoaXMgYXNzdW1wdGlvbiBoYXMgc2luY2UgYmVlbiB2YWxpZGF0ZWQgZm9yIHRoZSBDZWxpYWMgY2FzZXMgdmlhIG1vbGVjdWxhciBITEEgdHlwaW5nLgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZmVsc2UoSGV0ZXJvZGltZXJfQ29tYm8gPT0gIkRRMi41ICsgRFE4IiwgIkRRMi41L0RROCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZmVsc2UoSGV0ZXJvZGltZXJfQ29tYm8gPT0gIkRRMi41ICsgWCIsICJEUTIuNS9YIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZmVsc2UoSGV0ZXJvZGltZXJfQ29tYm8gPT0gIkRRMi41ICsgRFE3LjUiLCAiRFE3LjUvRFEyLjUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZmVsc2UoSGV0ZXJvZGltZXJfQ29tYm8gPT0gIkRROCArIERROCIsICJEUTgvRFE4IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmZWxzZShIZXRlcm9kaW1lcl9Db21ibyA9PSAiRFE4ICsgRFEyLjIiLCAiRFEyLjIvRFE4IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZmVsc2UoSGV0ZXJvZGltZXJfQ29tYm8gPT0gIkRRMi4yICsgLkRRMi4yIiwgIkRRMi4yL0RRMi4yIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWZlbHNlKEhldGVyb2RpbWVyX0NvbWJvID09ICJEUTggKyBEUTcuNSIsICJEUTcuNS9EUTgiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWZlbHNlKEhldGVyb2RpbWVyX0NvbWJvID09ICJEUTggKyBYIiwgIkRROC9YIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZmVsc2UoSGV0ZXJvZGltZXJfQ29tYm8gPT0gIkRRMi4yICsgWCIsICJEUTIuMi9YIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWZlbHNlKEhldGVyb2RpbWVyX0NvbWJvID09ICJEUTcuNSArIFgiLCAiRFE3LjUvWCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZmVsc2UoSGV0ZXJvZGltZXJfQ29tYm8gPT0gIkRRNy41ICsgRFE3LjUiLCAiRFE3LjUvRFE3LjUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmZWxzZShIZXRlcm9kaW1lcl9Db21ibyA9PSAiWCArIFgiLCAiWC9YIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgTkEpKSkpKSkpKSkpKSkpKSkpICU+JQogIHJlbmFtZShJbXB1dGVkX2hldGVyb2RpbWVycyA9IEhldGVyb2RpbWVyX0NvbWJvKSAlPiUKICBzZWxlY3QoSURfY29sbmFtZXMsIEVYQ0xVREVfZnJvbV9hbmFseXNpcywgRVhDTFVERV9yZWFzb24sCiAgICAgICAgIERRMi41LCBEUTgsIERRMi4yLCBEUTcuNSwgSW1wdXRlZF9oZXRlcm9kaW1lcnMsIEhMQV9EUV9HZW5vdHlwZSkKCiMjIyMgUHJpbnQgdGhlIGRhdGFmcmFtZToKZGYuSExBX0RRX0dlbm90eXBlCmBgYAoKIyMjIyBQcmludCB0aGUgaW1wdXRlZCBoZXRlcm9kaW1lcnMgYW5kIHRoZWlyIHRyYW5zbGF0aW9uIHRvIGFuIEhMQS1EUSBnZW5vdHlwZSBhcyByZXBvcnRlZCBpbiBTaGFycCBldCBhbC4sIDIwMTk6CmBgYHtyfQpTaGFycDIwMTlfR2Vub3R5cGVzIDwtIFRhYmxlX1MyICU+JQogIHNlbGVjdChgSExBLURRLkdlbm90eXBlYCkgJT4lCiAgcmVuYW1lKEhMQV9EUV9HZW5vdHlwZSA9IGBITEEtRFEuR2Vub3R5cGVgKSAlPiUKICBmaWx0ZXIoSExBX0RRX0dlbm90eXBlICE9ICIqIEZyZXF1ZW5jeSBpbiBjYXNlLWNvbnRyb2wgZGF0YSBhcyBjYWxsZWQgYnkgU05QIHN0cmF0ZWd5IChUYWJsZSAxKSIgJgogICAgICAgICAgIEhMQV9EUV9HZW5vdHlwZSAhPSAiVGFibGUgMTogSExBLURRIGdlbm90eXBlcyBpbmNsdWRlZCBpbiB0aGUgQ0QtR1JTIGJ5IG9kZHMgcmF0aW8iKSAlPiUKICBtdXRhdGUoSExBX0RRX0dlbm90eXBlID0gZmFjdG9yKEhMQV9EUV9HZW5vdHlwZSkpClNoYXJwMjAxOV9HZW5vdHlwZXMKCmRmLkhMQV9EUV9HZW5vdHlwZSAlPiUKICBmaWx0ZXIoaXMubmEoSW1wdXRlZF9oZXRlcm9kaW1lcnMpIHwgaXMubmEoSExBX0RRX0dlbm90eXBlKSkKCmFsbGVsZXNfdG9faGV0ZXJvZGltZXJzIDwtIGRmLmhldGVyb2RpbWVyX2Rvc2FnZSAlPiUKICBzZWxlY3QoRFFBMV8wNTpEUUIxXzAzMDEsIERRMi41OkRRNy41KSAlPiUKICB1bmlxdWUoKSAlPiUKICBnYXRoZXIoa2V5ID0gIkhldGVyb2RpbWVyIiwgdmFsdWUgPSAiUHJlc2VudEFic2VudCIsIERRMi41OkRRNy41KSAlPiUKICBzZWxlY3QoSGV0ZXJvZGltZXIsIFByZXNlbnRBYnNlbnQsIGV2ZXJ5dGhpbmcoKSkgJT4lCiAgZmlsdGVyKFByZXNlbnRBYnNlbnQgPT0gMSkKCmhldGVyb2RpbWVyc190b19ITEFnZW5vdHlwZSA8LSBTaGFycDIwMTlfR2Vub3R5cGVzICU+JQogIGxlZnRfam9pbihkZi5ITEFfRFFfR2Vub3R5cGUgJT4lCiAgICAgICAgICAgICAgc2VsZWN0KEltcHV0ZWRfaGV0ZXJvZGltZXJzLCBITEFfRFFfR2Vub3R5cGUpICU+JQogICAgICAgICAgICAgIHVuaXF1ZSgpLAogICAgICAgICAgICBieSA9IGMoIkhMQV9EUV9HZW5vdHlwZSIpKSAlPiUKICBzZWxlY3QoSW1wdXRlZF9oZXRlcm9kaW1lcnMsIEhMQV9EUV9HZW5vdHlwZSkgJT4lCiAgbXV0YXRlKEltcHV0ZWRfaGV0ZXJvZGltZXJzID0gaWZlbHNlKGlzLm5hKEltcHV0ZWRfaGV0ZXJvZGltZXJzKSwgIk4vQSAoTm90IG9ic2VydmVkIGluIEhUUCkiLCBJbXB1dGVkX2hldGVyb2RpbWVycykpCgphbGxlbGVzX3RvX2hldGVyb2RpbWVycwoKaGV0ZXJvZGltZXJzX3RvX0hMQWdlbm90eXBlCmBgYAoKIyMjIyBPdXRwdXQgYGhldGVyb2RpbWVyc190b19ITEFnZW5vdHlwZWAgdG8gQ1NWIGFuZCBUU1YKYGBge3J9CnNldHdkKGRpcikKZndyaXRlKGhldGVyb2RpbWVyc190b19ITEFnZW5vdHlwZSwgIkhMQV8wNDE4MjJfSW1wdXRlZF9ITEFfaGV0ZXJvZGltZXJzX3RvX0luZmVycmVkX0hMQURRX0dlbm90eXBlX3YwLjFfSlJTLmNzdiIpCmBgYAoKIyMjIyBWZXJpZnkgdGhhdCBlYWNoIEhUUF9JRCBpcyBvbmx5IGluY2x1ZGVkIGluIHRoaXMgZGF0YWZyYW1lIG9uY2UgYWZ0ZXIgRVhDTFVERSBmaWx0ZXIKYGBge3J9CmRmLkhMQV9EUV9HZW5vdHlwZSAlPiUKICBmaWx0ZXIoRVhDTFVERV9mcm9tX2FuYWx5c2lzID09IDApICU+JQogIGdyb3VwX2J5KFJlY29yZElEKSAlPiUKICBzdW1tYXJpc2UoTiA9IG4oKSkgJT4lCiAgYXJyYW5nZShkZXNjKE4pKSAlPiUKICBmaWx0ZXIoTiA+IDEpCgpwcmludCgiR29vZC4iKQpgYGAKCiMjIyMgVmVyaWZ5IHRoYXQgZWFjaCBwYXJ0aWNpcGFudCBoYXMgZXhhY3RseSBvbmUgSExBLURRIGdlbm90eXBlLCBhbmQgb25seSBvbmUgc3RhdHVzIHBlciBhbGxlbGUgKDAgb3IgMSk6CmBgYHtyfQpkZi5ITEFfRFFfR2Vub3R5cGUgJT4lCiAgc2VsZWN0KE1FR0EuSUlELCBITEFfRFFfR2Vub3R5cGUpICU+JQogIGdyb3VwX2J5KE1FR0EuSUlEKSAlPiUKICBzdW1tYXJpc2UoTiA9IG4oKSkgJT4lCiAgYXJyYW5nZShkZXNjKE4pKSAlPiUKICBmaWx0ZXIoTiAhPSAxIHwgaXMubmEoTikpCgpkZi5ITEFfRFFfR2Vub3R5cGUgJT4lCiAgc2VsZWN0KE1FR0EuSUlELCBITEFfRFFfR2Vub3R5cGUpICU+JQogIGdyb3VwX2J5KE1FR0EuSUlELCBITEFfRFFfR2Vub3R5cGUpICU+JQogIHN1bW1hcmlzZShOID0gbigpKSAlPiUKICBhcnJhbmdlKGRlc2MoTikpICU+JQogIGZpbHRlcihOICE9IDEgfCBpcy5uYShOKSkKCnByaW50KCJHb29kLCBlYWNoIHBhcnRpY2lwYW50IGhhcyBleGFjdGx5IG9uZSBITEEtRFEgZ2Vub3R5cGUsIGFuZCBvbmx5IG9uZSBzdGF0dXMgcGVyIGFsbGVsZSAoMCBvciAxKS4iKQpgYGAKCiMjIyMgUHJlcGFyZSBsb25nLWZvcm1hdCBkYXRhc2V0IG9mIEhMQS1EUSBnZW5vdHlwZXMgKDEgaWYgdGhlIHBlcnNvbiBoYXMgdGhlIGdlbm90eXBlLCAwIGlmIHRoZXkgaGF2ZSBzb21lIG90aGVyIGdlbm90eXBlKQpgYGB7cn0KZGYuSExBX0RRX0dlbm90eXBlLkxPTkcgPC0gZGYuSExBX0RRX0dlbm90eXBlICU+JQogIHNlbGVjdCgtYyhEUTIuNSwgRFE4LCBEUTIuMiwgRFE3LjUsIEltcHV0ZWRfaGV0ZXJvZGltZXJzKSkgJT4lCiAgdW5pcXVlKCkgJT4lCiAgbXV0YXRlKGR1bW15ID0gMSkgJT4lCiAgc3ByZWFkKGtleSA9IEhMQV9EUV9HZW5vdHlwZSwgdmFsdWUgPSBkdW1teSkgJT4lCiAgZ2F0aGVyKGtleSA9IEhMQV9EUV9HZW5vdHlwZSwgdmFsdWUgPSBEb3NhZ2UsIGBEUTIuMi9EUThgOmBYL1hgKSAlPiUKICBtdXRhdGUoRG9zYWdlID0gaWZlbHNlKGlzLm5hKERvc2FnZSksIDAsIERvc2FnZSkpCgpkZi5ITEFfRFFfR2Vub3R5cGUuTE9ORwpgYGAKCiMjIyMgUHJlcGFyZSBsb25nLWZvcm1hdCBkYXRhc2V0IG9mIEhMQS1EUSBnZW5vdHlwZXMgKDEgaWYgdGhlIHBlcnNvbiBoYXMgdGhlIGdlbm90eXBlLCAwIGlmIHRoZXkgaGF2ZSBzb21lIG90aGVyIGdlbm90eXBlKQpgYGB7cn0KdGVtcDEgPC0gZGYuSExBX0RRX0dlbm90eXBlICU+JQogIHNlbGVjdChJRF9jb2xuYW1lcywgRVhDTFVERV9mcm9tX2FuYWx5c2lzLCBFWENMVURFX3JlYXNvbiwgSExBX0RRX0dlbm90eXBlKSAlPiUKICBzZXBhcmF0ZShITEFfRFFfR2Vub3R5cGUsIGludG8gPSBjKCJIZXRlcm9kaW1lcjEiLCAiSGV0ZXJvZGltZXIyIiksIHNlcCA9ICIvIiwgZXh0cmEgPSAibWVyZ2UiLCByZW1vdmUgPSBUUlVFKSAlPiUKICBzZWxlY3QoSURfY29sbmFtZXMsIEVYQ0xVREVfZnJvbV9hbmFseXNpcywgRVhDTFVERV9yZWFzb24sIEhldGVyb2RpbWVyMSkgJT4lCiAgdW5pcXVlKCkgJT4lCiAgbXV0YXRlKEhldGVyb2RpbWVyX0luZGV4ID0gMSkgJT4lCiAgcmVuYW1lKEhldGVyb2RpbWVyID0gSGV0ZXJvZGltZXIxKQoKdGVtcDIgPC0gZGYuSExBX0RRX0dlbm90eXBlICU+JQogIHNlbGVjdChJRF9jb2xuYW1lcywgRVhDTFVERV9mcm9tX2FuYWx5c2lzLCBFWENMVURFX3JlYXNvbiwgSExBX0RRX0dlbm90eXBlKSAlPiUKICBzZXBhcmF0ZShITEFfRFFfR2Vub3R5cGUsIGludG8gPSBjKCJIZXRlcm9kaW1lcjEiLCAiSGV0ZXJvZGltZXIyIiksIHNlcCA9ICIvIiwgZXh0cmEgPSAibWVyZ2UiLCByZW1vdmUgPSBUUlVFKSAlPiUKICBzZWxlY3QoSURfY29sbmFtZXMsIEVYQ0xVREVfZnJvbV9hbmFseXNpcywgRVhDTFVERV9yZWFzb24sIEhldGVyb2RpbWVyMikgJT4lCiAgdW5pcXVlKCkgJT4lCiAgbXV0YXRlKEhldGVyb2RpbWVyX0luZGV4ID0gMikgJT4lCiAgcmVuYW1lKEhldGVyb2RpbWVyID0gSGV0ZXJvZGltZXIyKQoKZGYuSExBX2hldGVyb2RpbWVycy5MT05HIDwtIHJiaW5kKHRlbXAxLCB0ZW1wMikgJT4lCiAgZ3JvdXBfYnkoRmFtaWx5SUQsIFJlY29yZElELCBNRUdBLkZJRCwKICAgICAgICAgICBNRUdBLklJRCwgTUVHQS5MYWJJRCwKICAgICAgICAgICBFWENMVURFX2Zyb21fYW5hbHlzaXMsIEVYQ0xVREVfcmVhc29uLCBIZXRlcm9kaW1lcikgJT4lCiAgc3VtbWFyaXNlKERvc2FnZSA9IG4oKSkgJT4lCiAgc3ByZWFkKGtleSA9IEhldGVyb2RpbWVyLCB2YWx1ZSA9IERvc2FnZSkgJT4lCiAgZ2F0aGVyKERRMi4yOlgsIGtleSA9IEhldGVyb2RpbWVyLCB2YWx1ZSA9IERvc2FnZSkgJT4lCiAgbXV0YXRlKERvc2FnZSA9IGlmZWxzZShpcy5uYShEb3NhZ2UpLCAwLCBEb3NhZ2UpKQoKZGYuSExBX2hldGVyb2RpbWVycy5MT05HCmBgYAoKIyMjIyBSZW1vdmUgdGhlIGV4Y2x1c2lvbiBjb2x1bW5zOyB3ZSB3aWxsIGFkZCB0aGVtIGJhY2sgb25jZSB0aGUgYW5hbHlzaXMgbWV0YWRhdGEgaXMgZmluYWxpemVkCmBgYHtyfQpkZi5ITEFfRFFfR2Vub3R5cGUwMSA8LSBkZi5ITEFfRFFfR2Vub3R5cGUgJT4lCiAgc2VsZWN0KC1jKEVYQ0xVREVfZnJvbV9hbmFseXNpcywgRVhDTFVERV9yZWFzb24pKQoKZGYuSExBX0RRX0dlbm90eXBlLkxPTkcwMSA8LSBkZi5ITEFfRFFfR2Vub3R5cGUuTE9ORyAlPiUKICBzZWxlY3QoLWMoRVhDTFVERV9mcm9tX2FuYWx5c2lzLCBFWENMVURFX3JlYXNvbikpCgpkZi5ITEFfRFFfR2Vub3R5cGUwMQpkZi5ITEFfRFFfR2Vub3R5cGUuTE9ORzAxCmBgYAoKIyMjIyBPdXRwdXQgZGF0YXNldHMgdG8gQ1NWIGFuZCBUU1YKYGBge3J9CnNldHdkKGRpcikKZndyaXRlKGRmLkhMQV9EUV9HZW5vdHlwZTAxLCAiSExBXzA0MTgyMl9ITEFfRG9zYWdlX0hldGVyb2RpbWVyc19EUWdlbm90eXBlc19TaGFycERRNy41X3YwLjFfSlJTLmNzdiIpCmZ3cml0ZShkZi5ITEFfRFFfR2Vub3R5cGUwMSwgIkhMQV8wNDE4MjJfSExBX0Rvc2FnZV9IZXRlcm9kaW1lcnNfRFFnZW5vdHlwZXNfU2hhcnBEUTcuNV92MC4xX0pSUy50c3YiLCBzZXAgPSAiXHQiKQoKc2V0d2QoZGlyKQpmd3JpdGUoZGYuSExBX0RRX0dlbm90eXBlLkxPTkcwMSwgIkhMQV8wNDE4MjJfSExBX0dlbm90eXBlX0Rvc2FnZV9MT05HX3YwLjFfSlJTLmNzdiIpCmZ3cml0ZShkZi5ITEFfRFFfR2Vub3R5cGUuTE9ORzAxLCAiSExBXzA0MTgyMl9ITEFfR2Vub3R5cGVfRG9zYWdlX0xPTkdfdjAuMV9KUlMudHN2Iiwgc2VwID0gIlx0IikKCnNldHdkKGRpcikKZndyaXRlKGRmLkhMQV9EUV9HZW5vdHlwZS5MT05HMDEsICJITEFfMDQxODIyX0hMQV9IZXRlcm9kaW1lcnNfRG9zYWdlX0xPTkdfdjAuMV9KUlMuY3N2IikKZndyaXRlKGRmLkhMQV9EUV9HZW5vdHlwZS5MT05HMDEsICJITEFfMDQxODIyX0hMQV9IZXRlcm9kaW1lcnNfRG9zYWdlX0xPTkdfdjAuMV9KUlMudHN2Iiwgc2VwID0gIlx0IikKYGBgCg==